我有一张非常大的桌子,有近3亿条记录。由于select查询对我来说太慢了,我想把它拆分成大约800个小表。
数据集如下所示:
XXXXXX column2 column3 column4 ...
XXXXXX column2 column3 column4 ...
XXXXXX column2 column3 column4 ...
YYYYYY column2 column3 column4 ...
YYYYYY column2 column3 column4 ...
我想根据第一列的值拆分表格(例如记录XXXXXX
拆分成表XXXXXX
),最快的方法是什么?
注意:我已经为它添加了10个分区,但它并没有很好地加速它。
答案 0 :(得分:9)
在两种情况下,分区可以作为绩效策略:
该表的主要查询最终执行表或索引扫描,并且位于具有足够资源和适当配置的系统上,以实现高级并行性。因此,如果所有分区都在同一个物理驱动器上,那对你来说并没有多大帮助,那么就像你在第一时间那样绑定I / O.但是,如果您使用的是16核系统,并且每个分区位于物理上不同的磁盘上?分区可能会对系统性能产生惊人的改善。
分区规则使用的索引通常用于针对该表的最流行的查询。如果您要通过该路由获得性能,则应该对通常用于过滤或约束结果集的索引值进行分区。最常见的候选者是交易日期,因为报告通常是日历日期范围。然后,查询优化器可以使用分区规则来限制对单个(较小)分区的操作,或者并行运行两个或多个分区扫描(受上述相同限制)。
我认为想要拆分此表的主要原因是为了提高性能。但800分区?如果您正在追求性能提升,那可能是错误的方法。企业数据库在缓存内存中保留尽可能多的顶级表索引,以获得良好的性能。在五级b树中,对于适度使用的表,在第一次访问之后,前三个级别很可能始终保存在缓存中(这可能是具有整数主键的300M行表的配置) 。通过将表拆分为800个,这意味着将有800个数据结构试图保持缓存(除了表数据本身)。如果您的访问权限或多或少均匀地由主键分发,则在一个分区上搜索将最终推送其他分区 out 缓存,从而最终损害整体性能。< / p>
尽管如此,如果您决定这样做,将表分成N个部分的最简单方法是按照您希望针对主键(primary_key % 800
的分区数量的MODULUS对其进行分区。案件)。较新版本的MySQL也具有散列分区支持,使分区成任意数量的集合相当明确:
PARTITION BY HASH(some_column_value) PARTITIONS number_of_partitions
如果你想把你的数据放到800个实际的表中,你需要做这样的编辑魔术,或者使用脚本语言,并在SQL中执行:
CREATE TABLE table1 LIKE MasterTable
CREATE TABLE table2 LIKE MasterTable
CREATE TABLE table3 LIKE MasterTable
..
INSERT INTO table1 SELECT * FROM MasterTable WHERE id MOD 800 = 0
INSERT INTO table2 SELECT * FROM MasterTable WHERE id MOD 800 = 1
INSERT INTO table3 SELECT * FROM MasterTable WHERE id MOD 800 = 2
您可以使用动态SQL在您喜欢的编程语言的循环中执行此操作:这可能是最容易呈现的。
答案 1 :(得分:3)
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `split_tablebyrowscnt` (IN `tableName` VARCHAR(40), IN `step` INT) BEGIN
SET @table := tableName;
SET @liminf := 0;
SET @limsup := step;
SET @sql_2 = CONCAT('SELECT COUNT(*) INTO @rwcnt FROM ',@table,';');
PREPARE stmt from @sql_2;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
WHILE @liminf<@rwcnt DO
SET @sql_1 = CONCAT('SELECT CAST(',@limsup,' as char(10)) INTO @limsup_str;');
PREPARE stmt from @sql_1;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql_loop =CONCAT('CREATE TABLE ',@table,'_',@limsup_str,' SELECT * FROM(SELECT @rownum:=@rownum+1 rownum,d.* FROM (',@table,' d, (SELECT @rownum:=0) r))t
WHERE ( rownum >?) AND (rownum <= ?);');
PREPARE stmt from @sql_loop;
EXECUTE stmt USING @liminf,@limsup;
DEALLOCATE PREPARE stmt;
SET @sql_drop = CONCAT('ALTER TABLE ',@table,'_',@limsup_str,' DROP COLUMN rownum;');
PREPARE stmt from @sql_drop;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @liminf = @liminf + step;
SET @limsup = @limsup + step;
END WHILE ;
END$$
DELIMITER ;
执行程序:CALL split_tablebyrowscnt('myTable',100)