業務場景介紹
項目中需要記錄全量(新增,編輯,刪除,查詢都需要記錄變更項已經標題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()