MySQL:将大表拆分成小表的最快方法

时间:2013-07-28 04:56:42

标签: mysql

我有一张非常大的桌子,有近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个分区,但它并没有很好地加速它。

2 个答案:

答案 0 :(得分:9)

在两种情况下,分区可以作为绩效策略:

  1. 该表的主要查询最终执行表或索引扫描,并且位于具有足够资源和适当配置的系统上,以实现高级并行性。因此,如果所有分区都在同一个物理驱动器上,那对你来说并没有多大帮助,那么就像你在第一时间那样绑定I / O.但是,如果您使用的是16核系统,并且每个分区位于物理上不同的磁盘上?分区可能会对系统性能产生惊人的改善。

  2. 分区规则使用的索引通常用于针对该表的最流行的查询。如果您要通过该路由获得性能,则应该对通常用于过滤或约束结果集的索引值进行分区。最常见的候选者是交易日期,因为报告通常是日历日期范围。然后,查询优化器可以使用分区规则来限制对单个(较小)分区的操作,或者并行运行两个或多个分区扫描(受上述相同限制)。

  3. 我认为想要拆分此表的主要原因是为了提高性能。但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)

感谢每位为我的存储过程提供灵感的人! 我很高兴与您分享:enter image description here

    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)