Mysql Merge分表的研究备忘
发布于 8 年前 作者 zouzhenxing 4423 次浏览 来自 分享

为什么要分表 当一张表的数据过多、数据文件过大时,会影响到数据的性能。将一张表拆分多张表是一个非常好办法。作为一个程序员,我希望有数据库能提供分表有如下几个功能: 1、自动分表 2、可自由拆分 3、可以跨磁盘存放 虽然我只一个程序员,了解并掌握这样的数据库技术会给我在写代码时多几条思路。

Mysql Merge分表 Mysql提供Merge存储引擎来进行分表,Merge表能将相同结构的表组合起来当做一张逻辑表来使用。代码如下:

create table t1(
   id char(19) primary key,
   uname varchar(255),
   upassword varchar(255)
)ENGINE = MyISAM;

create table t2(
   id char(19) primary key,
   uname varchar(255),
   upassword varchar(255)
)ENGINE = MyISAM;

create table test(
   id char(19) primary key,
   uname varchar(255),
   upassword varchar(255)
)ENGINE = MERGE union(t1,t2) insert_method=last;

看上面代码,是不是觉得Merge有点与View相同,其实我也搞不清二者有什么区别,理论上分表还是使用Merge比较好,这也是官方推荐的方式。 使用Merge表有如下几个注意点: 1、所有分表都必须是MyISAM引擎,MyISAN引擎是不支持事务的。 2、Merge表只保证合表后数据唯一性,合表前的数据可能会存在重复。 3、删除分表正确的做法是 ALTER TABLE test UNION (t1); drop table t2;如果直接删除t2,test表会报错。 4、不能自动分表,需要定期维护。

编写自动分表脚本 在使用Merge表过程,最头痛的就是不能自动分表,靠人工定时清理肯定不合理的,在实践过程中,我想到了两种办法来解决这个问题。 第一、使用Mysql Event按时间周期自动建表 第二、分表到达一定数量后自动建表 具体实现思路如下: 首先模似Oracle数据库sequence

--创建自定义序列表
DROP TABLE IF EXISTS sequence;  
CREATE TABLE sequence (  
         name VARCHAR(50) NOT NULL,  
         current_value INT NOT NULL,  
         increment INT NOT NULL DEFAULT 1,  
         PRIMARY KEY (name)  
) ENGINE=InnoDB;

--定义取当前序列函数
DROP FUNCTION IF EXISTS currval;
CREATE FUNCTION currval (seq_name VARCHAR(50))  
         RETURNS INTEGER  
         LANGUAGE SQL  
         DETERMINISTIC  
         CONTAINS SQL  
         SQL SECURITY DEFINER  
         COMMENT ''  
BEGIN  
         DECLARE value INTEGER;  
         SET value = 0;  
         SELECT current_value INTO value  
                   FROM sequence  
                   WHERE name = seq_name;  
         RETURN value;  
END; 

--定义取下一值函数
DROP FUNCTION IF EXISTS nextval;  
CREATE FUNCTION nextval (seq_name VARCHAR(50))  
         RETURNS INTEGER  
         LANGUAGE SQL  
         DETERMINISTIC  
         CONTAINS SQL  
         SQL SECURITY DEFINER  
         COMMENT ''  
BEGIN  
         UPDATE sequence  
                   SET current_value = current_value + increment  
                   WHERE name = seq_name;  
         RETURN currval(seq_name);  
END;

--定义更新值函数
DROP FUNCTION IF EXISTS setval;  
CREATE FUNCTION setval (seq_name VARCHAR(50), value INTEGER)  
         RETURNS INTEGER  
         LANGUAGE SQL  
         DETERMINISTIC  
         CONTAINS SQL  
         SQL SECURITY DEFINER  
         COMMENT ''  
BEGIN  
         UPDATE sequence  
                   SET current_value = value  
                   WHERE name = seq_name;  
         RETURN currval(seq_name);  
END;

再创建分表与Merge表

--创建第一张分表
create table t1(
   id char(19) primary key,
   uname varchar(255),
   upassword varchar(255)
)ENGINE = MyISAM;


--创建merge表
drop table if EXISTS test;
create table test(
   id char(19) primary key,
   uname varchar(255),
   upassword varchar(255)
)ENGINE = MERGE union(t1) insert_method=last;

--增加分表sequence
insert into sequence values('test',1,1);

针对Merge表创建一个增加数据存储过程testadd,这个过程的思路是第一次增加都查看当前表的数量,如果超过了20W,就创建一张新表,然后UNION到test表中。

drop PROCEDURE if EXISTS testadd;
create PROCEDURE testadd(uname varchar(255),upassword varchar(255))
BEGIN
	DECLARE num INT;
	DECLARE tname VARCHAR (255);
	DECLARE i INT DEFAULT 1;
	DECLARE uniontb VARCHAR (255) DEFAULT '';

	insert into test values(UUID_SHORT(),uname,upassword);
	
	SELECT count(*) INTO num FROM test;	
	IF MOD(num,200000) = 0 THEN
		SET tname = CONCAT('t', nextval('test'));
		set @sqlstr = CONCAT('create table ',tname,' like t1');
		prepare stmt from @sqlstr;
		EXECUTE stmt;
    deallocate prepare stmt;
		
		WHILE i <= currval ('test') DO
			SET uniontb = CONCAT(uniontb, CONCAT('t', i), ',');
			SET i = i + 1;
		END WHILE;

		set uniontb = LEFT(uniontb,LENGTH(uniontb) - 1);
		set @sqlstr = CONCAT('ALTER TABLE test ENGINE = MERGE UNION = (',uniontb,') INSERT_METHOD = LAST');
   	prepare stmt from @sqlstr;
   	EXECUTE stmt;
    deallocate prepare stmt;
	END IF;
	
end

查看结果 100W自动分表插入 untitled1.png 100W不分表插入 untitled2.png 很明显,在使用自动分表存储过程后,数据插入性能下降很多。最佳办法是按周期建表

--创建分表
drop PROCEDURE if EXISTS createtb;
create PROCEDURE createtb()
BEGIN
	DECLARE tname VARCHAR (255);
	DECLARE i INT DEFAULT 1;
	DECLARE uniontb VARCHAR (255) DEFAULT '';
	
	SET tname = CONCAT('t', nextval('test'));
	set @sqlstr = CONCAT('create table ',tname,' like t1');
	prepare stmt from @sqlstr;
	EXECUTE stmt;
	deallocate prepare stmt;
	
	WHILE i <= currval ('test') DO
		SET uniontb = CONCAT(uniontb, CONCAT('t', i), ',');
		SET i = i + 1;
	END WHILE;

	set uniontb = LEFT(uniontb,LENGTH(uniontb) - 1);
	set @sqlstr = CONCAT('ALTER TABLE test ENGINE = MERGE UNION = (',uniontb,') INSERT_METHOD = LAST');
	prepare stmt from @sqlstr;
	EXECUTE stmt;
	deallocate prepare stmt;
end
--创建事件
CREATE EVENT test_event
    ON SCHEDULE 
        EVERY 1 DAY
    STARTS date_add(date(curdate() + 1),interval 3 hour)
    DO call createtb;

这两种办法都可以实现自动分表,具体情况可以具体使用。 测试自动分表 由于时间关系我先用了第一种分表办法

create PROCEDURE test()
begin
	DECLARE i int DEFAULT 1;
	while i < 5000000 DO
		call testadd( CONCAT('test',i),'test' );
	end while;
end;

结果如下: untitled3.png 一共25张表,每表20W数据

回到顶部