mysql merge 引擎分表

業務場景介紹

項目中需要記錄全量(新增,編輯,刪除,查詢都需要記錄變更項已經標題ID等)日誌,要記錄的特別的詳細,內容就會有點多,如果我們只用一張表來進行存儲的話,要不了多久我們的表就會被撐爆,會影響效率,特別是查詢。爲了保障效率,我們就需要做優化,那麼我們想到了分表來存儲我們的日誌。

分表的實現

那麼怎麼來實現呢,最起碼我們要新建一張子表,還有一張主表。
在實現之前我們先確定好我們的分表策略,如果我們用時間來進行分表,目前業務剛剛開始,沒有人知道以後會是什麼情況,多少人會使用,使用的頻率高不高,每天大概會產生多少數據,我們都沒法預知,那麼我們乾脆一點,不按照時間了,我們的想法是直接按照數據量來進行分表,比如子表的數據量達到了100萬(具體值可以調整)就新建一張新的子表。

子表的創建

策略定好了我們就開始創建了

CREATE TABLE `sys_full_dose_log_1` (
  `id` bigint(20) NOT NULL COMMENT '主鍵(雪花串)',
  `user_id` varchar(55) DEFAULT NULL COMMENT '操作用戶編號',
  `user_name` varchar(50) DEFAULT NULL COMMENT '操作用戶名稱',
  `operate_at` datetime DEFAULT NULL COMMENT '操作時間',
  `old_data` text COMMENT '修改之前的json值',
  `new_data` text COMMENT '修改之後的json值',
  `route` varchar(120) DEFAULT NULL COMMENT '路徑',
  `operate_type` varchar(100) DEFAULT NULL COMMENT '操作類型',
  `rdescribe` varchar(255) DEFAULT NULL COMMENT '操作描述',
  `ip` varchar(30) DEFAULT NULL COMMENT '操作IP',
  `business` varchar(255) DEFAULT NULL COMMENT '業務名稱',
  `num` int(11) DEFAULT NULL COMMENT '數量',
  `entry_key` varchar(50) DEFAULT NULL COMMENT '條目名',
  `entry_value` varchar(50) DEFAULT NULL COMMENT '條目值',
  `entry_id` varchar(55) DEFAULT NULL COMMENT '條目id',
  `filed_type` varchar(30) DEFAULT NULL COMMENT '字段類型',
  `is_success` tinyint(4) DEFAULT '1' COMMENT '操作結果:1成功;0失敗',
  `log_type` int(11) DEFAULT NULL COMMENT '日誌類型:1登錄/退出;2部門日誌;3用戶日誌;4權限日誌;5角色日誌',
  `reason` text COMMENT '執行失敗原因',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='全量日誌表';

這裏存儲引擎一定要選擇MyISAM。

主表的創建

CREATE TABLE `sys_full_dose_log_body` (
  `id` bigint(20) NOT NULL COMMENT '主鍵(雪花串)',
  `user_id` varchar(55) DEFAULT NULL COMMENT '操作用戶編號',
  `user_name` varchar(50) DEFAULT NULL COMMENT '操作用戶名稱',
  `operate_at` datetime DEFAULT NULL COMMENT '操作時間',
  `old_data` text COMMENT '修改之前的json值',
  `new_data` text COMMENT '修改之後的json值',
  `route` varchar(120) DEFAULT NULL COMMENT '路徑',
  `operate_type` varchar(100) DEFAULT NULL COMMENT '操作類型',
  `rdescribe` varchar(255) DEFAULT NULL COMMENT '操作描述',
  `ip` varchar(30) DEFAULT NULL COMMENT '操作IP',
  `business` varchar(255) DEFAULT NULL COMMENT '業務名稱',
  `num` int(11) DEFAULT NULL COMMENT '數量',
  `entry_key` varchar(50) DEFAULT NULL COMMENT '條目名',
  `entry_value` varchar(50) DEFAULT NULL COMMENT '條目值',
  `entry_id` varchar(55) DEFAULT NULL COMMENT '條目id',
  `filed_type` varchar(30) DEFAULT NULL COMMENT '字段類型',
  `is_success` tinyint(4) DEFAULT '1' COMMENT '操作結果:1成功;0失敗',
  `log_type` int(11) DEFAULT NULL COMMENT '日誌類型:日誌類型:1登錄/退出;2部門日誌;3用戶日誌;4權限日誌;5角色日誌',
  `reason` text COMMENT '執行失敗原因',
  PRIMARY KEY (`id`)
) ENGINE=MRG_MyISAM DEFAULT CHARSET=utf8 INSERT_METHOD=LAST UNION=(`sys_full_dose_log_1`) COMMENT='系統全量日誌表';

這裏主表的存儲引擎必須是MRG_MyISAM,如果你有多張子表,那麼你可以在上面新增多張子表然後(sys_full_dose_log_1,sys_full_dose_log_2)用逗號分割

測試

我們可以直接操作主表sys_full_dose_log_body,直接查詢主表時,會自動匹配所有子表的數據。當我們直接插入主表的時候,默認會插入UNION裏面的最後一張子表,當然我們也可以直接插入某一張子表。需要注意的是MRG_MyISAM這個存儲引擎是沒有事務的,在程序中不要考慮回滾的事情。

插入

插入主表

INSERT INTO `sys_full_dose_log_body`(`id`, `user_id`, `user_name`, `operate_at`, `old_data`, `new_data`, `route`, `operate_type`, `rdescribe`, `ip`, `business`, `num`, `entry_key`, `entry_value`, `entry_id`, `filed_type`, `is_success`, `log_type`, `reason`) VALUES (592739202418868224, NULL, NULL, '2019-07-03 16:33:58', NULL, NULL, '', '登錄', '登錄', '192.168.51.243', 'DEFAULT', 0, '', NULL, NULL, NULL, 0, 1, NULL);

插入子表

INSERT INTO `sys_full_dose_log_1`(`id`, `user_id`, `user_name`, `operate_at`, `old_data`, `new_data`, `route`, `operate_type`, `rdescribe`, `ip`, `business`, `num`, `entry_key`, `entry_value`, `entry_id`, `filed_type`, `is_success`, `log_type`, `reason`) VALUES (593082590901841920, NULL, '小的明', '2019-07-03 17:18:29', NULL, NULL, '安全-用戶管理', '查看', '查看用戶', '127.0.0.1', 'USER', 0, 'nickName', NULL, NULL, NULL, 1, 3, NULL);

查詢

在這裏插入圖片描述
這裏大家可以多創建幾張子表來進行測試,也可以在表中多插入一點數據來進行測試,然後在覺得要不要使用這種方法。

動態創建表

思路上面已經說了,當表的數量達到100萬後我們就新增另一張子表來進行存儲,下面給出動態創建子表的存儲過程

動態建表存儲過程

CREATE DEFINER=`root`@`%` PROCEDURE `PROC_CREATE_LOG_TABLE`()
BEGIN
	/*
	 * ===================新增日誌子表存儲過程====================
	 */
	 
	#聲明變量
	
	-- 當前最後一個子表的表名
	DECLARE v_last_table_name VARCHAR(30);
	-- 當前要新增的子表的後綴名
	DECLARE v_last_table_count INT;
	-- 當前子表最大id
	DECLARE v_max_count BIGINT;
	-- 當前子表最大id查詢語句
	DECLARE v_max_count_sql VARCHAR(300);
	-- 當前子表集合字符串(用','分割)
	DECLARE v_union_table TEXT;
	-- 當前要創建的表名前綴
	DECLARE v_create_table_name VARCHAR(50) DEFAULT 'sys_full_dose_log_';
	-- 當前數據庫名
	DECLARE v_schema VARCHAR(50) DEFAULT 'ktwlsoft_framework_basic_pl';
	
	SELECT table_name INTO v_last_table_name FROM information_schema.TABLES WHERE table_schema = v_schema AND TABLE_NAME LIKE CONCAT(v_create_table_name,'%') AND ENGINE = 'MyISAM'
ORDER BY create_time DESC LIMIT 1;
	SELECT REVERSE(LEFT(REVERSE(v_last_table_name),INSTR(REVERSE(v_last_table_name),'_')-1)) + 1 INTO v_last_table_count; 
	
	# 需要聲明全局變量(@...)來接受動態SQL的值
	SET v_max_count_sql = CONCAT(' ', 'select count(id) into @max_count from ',v_last_table_name);
	SET @sql = v_max_count_sql;
	PREPARE s1 FROM @sql; 
	EXECUTE s1; 
	DEALLOCATE PREPARE s1;
	SET v_max_count = @max_count;
	SELECT v_max_count;
	#當數據大於等於一百萬,從新創建子表並將子表union到主表中
	IF v_max_count >= 1000000 THEN
		
		#創建子表
		SET @sql_create_table = CONCAT(
		'CREATE TABLE IF NOT EXISTS ',v_create_table_name,v_last_table_count,'(
			id bigint(20) comment \'主鍵(雪花串)\',
			user_id varchar(55) comment \'操作用戶編號\',
			user_name varchar(50) comment \'操作用戶名稱\',
			operate_at datetime comment \'操作時間\',
			old_data text comment \'修改之前的json值\',
			new_data text comment \'修改之後的json值\',
			route	varchar(120) comment \'路徑\',
			operate_type varchar(100) comment \'操作類型\',
			rdescribe varchar(255) comment \'操作描述\',
			ip varchar(30) comment \'操作IP\',
			business varchar(255) comment \'業務名稱\',
			num int comment \'數量\',
			entry_key varchar(50) comment \'條目名\',
			entry_value varchar(50) comment \'條目值\',
			entry_id varchar(55) comment \'條目id\',
			filed_type varchar(30) comment \'字段類型\',
			is_success tinyint default 1 comment \'操作結果:1成功;0失敗\',
			log_type int comment \'日誌類型:1登錄/退出;2部門日誌;3用戶日誌;4權限日誌;5角色日誌\',
			reason text comment \'執行失敗原因\',
			PRIMARY KEY(id)
			) comment \'全量日誌表\' ENGINE = MYISAM DEFAULT CHARSET = utf8');
		PREPARE sql_create_table FROM @sql_create_table;
		EXECUTE sql_create_table;
		DEALLOCATE PREPARE sql_create_table;
		
		#查詢子表字符串集合
		SELECT GROUP_CONCAT(table_name ORDER BY create_time ASC,',') INTO v_union_table FROM information_schema.TABLES WHERE table_schema = v_schema AND TABLE_NAME LIKE CONCAT(v_create_table_name,'%') AND ENGINE = 'MyISAM'; 
		
		SELECT v_union_table;
		
		#更新主表
		SET @sql_alter_table = CONCAT('alter table ',v_create_table_name,'body UNION = (',v_union_table,')');
		PREPARE sql_alter_table FROM @sql_alter_table;
		EXECUTE sql_alter_table;
		#釋放執行中使用的所有數據庫資源
		DEALLOCATE PREPARE sql_alter_table;
		
	END IF;
	
END

Mysql新建定時計劃

新建計劃定時調用上面創建的存儲過程

CREATE DEFINER=`root`@`%` EVENT `PLAN_CHECK_LOG_TABLE` 
ON SCHEDULE EVERY 30 MINUTE 
STARTS '開始檢查時間(yyyy-MM-dd HH:mm:ss)' 
ON COMPLETION PRESERVE ENABLE COMMENT '半小時檢查一次日誌子表是否需要新增' 
DO 
call PROC_CREATE_LOG_TABLE()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章