Files
im_server/docs/sql/IM-共库核心分表-测试预演库.sql
T

273 lines
11 KiB
SQL

-- 仅用于测试环境复制库 meeting_im_sharding_preview。
-- 源库 meeting_im 和注册库 hotgo 不在本脚本修改范围内。
USE `meeting_im_sharding_preview`;
-- 1. 共享业务表补充商户维度,复制库全部现有数据归属商户 2。
DELIMITER $$
DROP PROCEDURE IF EXISTS add_preview_tenant_columns$$
CREATE PROCEDURE add_preview_tenant_columns()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE target_table VARCHAR(128);
DECLARE has_id INT DEFAULT 0;
DECLARE table_cursor CURSOR FOR
SELECT t.table_name
FROM information_schema.tables t
WHERE t.table_schema = 'meeting_im_sharding_preview'
AND t.table_type = 'BASE TABLE'
AND t.table_name LIKE 'im\_%'
AND NOT EXISTS (
SELECT 1 FROM information_schema.columns c
WHERE c.table_schema=t.table_schema
AND c.table_name=t.table_name
AND c.column_name='tenant_id'
);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN table_cursor;
table_loop: LOOP
FETCH table_cursor INTO target_table;
IF done = 1 THEN
LEAVE table_loop;
END IF;
SELECT COUNT(*) INTO has_id
FROM information_schema.columns
WHERE table_schema='meeting_im_sharding_preview'
AND table_name=target_table AND column_name='id';
SET @position_sql = IF(has_id > 0, ' AFTER `id`', ' FIRST');
SET @alter_sql = CONCAT('ALTER TABLE `meeting_im_sharding_preview`.`', target_table,
'` ADD COLUMN `tenant_id` BIGINT NOT NULL DEFAULT 2 COMMENT ''商户ID''',
@position_sql);
PREPARE stmt FROM @alter_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE table_cursor;
END$$
DELIMITER ;
CALL add_preview_tenant_columns();
DROP PROCEDURE IF EXISTS add_preview_tenant_columns;
-- 2. 群消息路由元数据的唯一性增加商户维度。
ALTER TABLE `im_group_message_shards`
DROP PRIMARY KEY,
DROP INDEX `uk_im_group_message_shards_table`,
DROP INDEX `idx_im_group_message_shards_status`,
ADD PRIMARY KEY (`tenant_id`,`shard_month`),
ADD UNIQUE INDEX `uk_im_group_message_shards_table` (`tenant_id`,`table_name`),
ADD INDEX `idx_im_group_message_shards_status` (`tenant_id`,`status`,`shard_month`);
DROP TABLE IF EXISTS `im_group_message_topic_shards`;
DROP TABLE IF EXISTS `im_group_message_dedup`;
-- 3. 创建商户 2 的好友与单聊消息分表并复制快照数据。
CREATE TABLE `im_002_friendships` LIKE `im_friendships`;
ALTER TABLE `im_002_friendships` MODIFY `tenant_id` BIGINT NOT NULL DEFAULT 2 COMMENT '商户ID';
INSERT INTO `im_002_friendships` SELECT * FROM `im_friendships` WHERE `tenant_id`=2;
CREATE TABLE `im_002_messages` LIKE `im_messages`;
ALTER TABLE `im_002_messages` MODIFY `tenant_id` BIGINT NOT NULL DEFAULT 2 COMMENT '商户ID';
INSERT INTO `im_002_messages` SELECT * FROM `im_messages` WHERE `tenant_id`=2;
-- 4. 将所有现存标准月表复制为商户 2 的月表。
DELIMITER $$
DROP PROCEDURE IF EXISTS migrate_preview_group_month_tables$$
CREATE PROCEDURE migrate_preview_group_month_tables()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE source_table VARCHAR(64);
DECLARE month_code CHAR(6);
DECLARE target_table VARCHAR(64);
DECLARE month_cursor CURSOR FOR
SELECT table_name
FROM information_schema.tables
WHERE table_schema='meeting_im_sharding_preview'
AND table_name REGEXP '^im_group_messages_[0-9]{6}$';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN month_cursor;
month_loop: LOOP
FETCH month_cursor INTO source_table;
IF done = 1 THEN
LEAVE month_loop;
END IF;
SET month_code = RIGHT(source_table, 6);
SET target_table = CONCAT('im_002_group_messages_', month_code);
SET @create_sql = CONCAT('CREATE TABLE `', target_table, '` LIKE `', source_table, '`');
PREPARE stmt FROM @create_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @alter_sql = CONCAT('ALTER TABLE `', target_table,
'` MODIFY `tenant_id` BIGINT NOT NULL DEFAULT 2 COMMENT ''商户ID''');
PREPARE stmt FROM @alter_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @copy_sql = CONCAT('INSERT INTO `', target_table, '` SELECT * FROM `', source_table,
'` WHERE `tenant_id`=2');
PREPARE stmt FROM @copy_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE month_cursor;
END$$
DELIMITER ;
CALL migrate_preview_group_month_tables();
DROP PROCEDURE IF EXISTS migrate_preview_group_month_tables;
UPDATE `im_group_message_shards`
SET `table_name`=CONCAT('im_002_group_messages_',`shard_month`)
WHERE `tenant_id`=2;
-- 5. 补齐 default 商户(ID=1,分表编号 001)的核心分表。
-- tinode 是 default 商户的原库,只从该库读取,不修改原表。
CREATE TABLE `im_001_friendships` LIKE `im_friendships`;
ALTER TABLE `im_001_friendships` MODIFY `tenant_id` BIGINT NOT NULL DEFAULT 1 COMMENT '商户ID';
INSERT INTO `im_001_friendships` (
`id`,`tenant_id`,`user_id`,`peer_id`,`status`,`remark`,`topic`,`created_at`,`updated_at`
)
SELECT `id`,1,`user_id`,`peer_id`,`status`,`remark`,`topic`,`created_at`,`updated_at`
FROM `tinode`.`im_friendships`;
CREATE TABLE `im_001_messages` LIKE `im_messages`;
ALTER TABLE `im_001_messages` MODIFY `tenant_id` BIGINT NOT NULL DEFAULT 1 COMMENT '商户ID';
INSERT INTO `im_001_messages` (
`id`,`tenant_id`,`createdat`,`updatedat`,`deletedat`,`delid`,`seqid`,`topic`,`from`,
`client_msg_id`,`version`,`recalled_at`,`recalled_by`,`head`,`content`,`content_nonce`,`key_version`
)
SELECT `id`,1,`createdat`,`updatedat`,`deletedat`,`delid`,`seqid`,`topic`,`from`,
`client_msg_id`,`version`,`recalled_at`,`recalled_by`,`head`,`content`,`content_nonce`,`key_version`
FROM `tinode`.`im_messages`;
DELIMITER $$
DROP PROCEDURE IF EXISTS migrate_default_group_month_tables$$
CREATE PROCEDURE migrate_default_group_month_tables()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE source_table VARCHAR(64);
DECLARE month_code CHAR(6);
DECLARE target_table VARCHAR(64);
DECLARE month_cursor CURSOR FOR
SELECT table_name
FROM information_schema.tables
WHERE table_schema='tinode'
AND table_name REGEXP '^im_group_messages_[0-9]{6}$';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN month_cursor;
month_loop: LOOP
FETCH month_cursor INTO source_table;
IF done = 1 THEN
LEAVE month_loop;
END IF;
SET month_code = RIGHT(source_table, 6);
SET target_table = CONCAT('im_001_group_messages_', month_code);
SET @create_sql = CONCAT('CREATE TABLE `', target_table,
'` LIKE `im_group_messages_', month_code, '`');
PREPARE stmt FROM @create_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @alter_sql = CONCAT('ALTER TABLE `', target_table,
'` MODIFY `tenant_id` BIGINT NOT NULL DEFAULT 1 COMMENT ''商户ID''');
PREPARE stmt FROM @alter_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @copy_sql = CONCAT(
'INSERT INTO `', target_table, '` (`tenant_id`,`message_id`,`topic`,`group_id`,`seqid`,',
'`from_user`,`client_msg_id`,`msg_type`,`status`,`content`,`content_nonce`,`key_version`,',
'`head`,`mentioned_user_ids`,`mention_all`,`reply_seqid`,`root_seqid`,`file_count`,',
'`content_size`,`has_risk`,`audit_status`,`version`,`recalled_at`,`recalled_by`,',
'`deleted_at`,`expires_at`,`created_at`,`updated_at`) ',
'SELECT 1,`message_id`,`topic`,`group_id`,`seqid`,`from_user`,`client_msg_id`,`msg_type`,',
'`status`,`content`,`content_nonce`,`key_version`,`head`,`mentioned_user_ids`,`mention_all`,',
'`reply_seqid`,`root_seqid`,`file_count`,`content_size`,`has_risk`,`audit_status`,`version`,',
'`recalled_at`,`recalled_by`,`deleted_at`,`expires_at`,`created_at`,`updated_at` ',
'FROM `tinode`.`', source_table, '`'
);
PREPARE stmt FROM @copy_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE month_cursor;
END$$
DELIMITER ;
CALL migrate_default_group_month_tables();
DROP PROCEDURE IF EXISTS migrate_default_group_month_tables;
INSERT INTO `im_group_message_shards` (
`tenant_id`,`shard_month`,`table_name`,`shard_start`,`shard_end`,`status`,`row_count`,
`min_created_at`,`max_created_at`,`created_at`,`updated_at`
)
SELECT 1,`shard_month`,CONCAT('im_001_group_messages_',`shard_month`),`shard_start`,`shard_end`,
`status`,`row_count`,`min_created_at`,`max_created_at`,`created_at`,`updated_at`
FROM `tinode`.`im_group_message_shards`;
-- Topic-to-month routes are Redis-backed and are rebuilt by runtime reads.
-- 以实际复制后的物理表为准刷新统计,避免源库路由统计滞后。
DELIMITER $$
DROP PROCEDURE IF EXISTS refresh_default_group_shard_stats$$
CREATE PROCEDURE refresh_default_group_shard_stats()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE month_code CHAR(6);
DECLARE target_table VARCHAR(64);
DECLARE shard_cursor CURSOR FOR
SELECT `shard_month`
FROM `im_group_message_shards`
WHERE `tenant_id`=1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN shard_cursor;
shard_loop: LOOP
FETCH shard_cursor INTO month_code;
IF done = 1 THEN
LEAVE shard_loop;
END IF;
SET target_table = CONCAT('im_001_group_messages_', month_code);
SET @stats_sql = CONCAT(
'UPDATE `im_group_message_shards` SET ',
'`row_count`=(SELECT COUNT(*) FROM `', target_table, '`),',
'`min_created_at`=(SELECT MIN(`created_at`) FROM `', target_table, '`),',
'`max_created_at`=(SELECT MAX(`created_at`) FROM `', target_table, '`) ',
'WHERE `tenant_id`=1 AND `shard_month`=''', month_code, ''''
);
PREPARE stmt FROM @stats_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE shard_cursor;
END$$
DELIMITER ;
CALL refresh_default_group_shard_stats();
DROP PROCEDURE IF EXISTS refresh_default_group_shard_stats;
-- 6. 验证:missing_tenant_id 必须为 0,两个商户的分表与路由均存在。
SELECT COUNT(*) AS missing_tenant_id
FROM information_schema.tables t
WHERE t.table_schema='meeting_im_sharding_preview'
AND t.table_type='BASE TABLE'
AND t.table_name LIKE 'im\_%'
AND NOT EXISTS (
SELECT 1 FROM information_schema.columns c
WHERE c.table_schema=t.table_schema
AND c.table_name=t.table_name
AND c.column_name='tenant_id'
);
SELECT 'friendships' AS data_type,
(SELECT COUNT(*) FROM `im_friendships` WHERE `tenant_id`=2) AS source_count,
(SELECT COUNT(*) FROM `im_002_friendships`) AS target_count
UNION ALL
SELECT 'messages',
(SELECT COUNT(*) FROM `im_messages` WHERE `tenant_id`=2),
(SELECT COUNT(*) FROM `im_002_messages`);
SELECT s.`shard_month`,s.`table_name`,s.`status`,s.`row_count`
FROM `im_group_message_shards` s
WHERE s.`tenant_id` IN (1,2)
ORDER BY s.`tenant_id`,s.`shard_month`;
SELECT t.table_name
FROM information_schema.tables t
WHERE t.table_schema='meeting_im_sharding_preview'
AND t.table_name REGEXP '^im_(001|002)_(friendships|messages|group_messages_[0-9]{6})$'
ORDER BY t.table_name;