如何使用tokudb每秒多做50.000次插入?

时间:2016-10-07 22:05:31

标签: mysql percona tokudb

目前我正在测试TokuDB,我印象非常深刻。此时,每秒插入数量达到峰值,刚好超过每秒50,000次,同时运行了两个同步工作。平均插入率为每秒38.000至42.000次插入。

我想更高,每秒100,000次插入,因为我现在需要插入12亿个计算行,在不久的将来需要大约60亿个。我想就如何实现这一点提出一些建议: - )

我目前的设置:

  1. 硬件:带4GB内存,150GB SSD,2核的VPS:Intel Westmere E56xx / L56xx / X56xx(Nehalem-C)2.59GHz CPU
  2. 磁盘装入选项:默认值,noatime
  3. 操作系统:CentOS 6.8 64位
  4. 数据库:Percona Server 5.7.14-8
  5. My.cnf设置:

    # TokuDB #
    tokudb_cache_size = 2G
    tokudb_commit_sync = 0
    tokudb_fsync_log_period = 1000
    

    TokuDB表格布局:

    CREATE TABLE `t1` (
      `id` int(15) NOT NULL AUTO_INCREMENT,
      `m_id` int(11) NOT NULL,
      `c1` decimal(6,2) DEFAULT NULL,
      `c2` decimal(6,2) DEFAULT NULL,
      `c3` decimal(6,2) DEFAULT NULL,
      `c4` decimal(6,2) DEFAULT NULL,
      `c5` decimal(6,2) DEFAULT NULL,
      `c6` decimal(6,2) DEFAULT NULL,
      `c7` decimal(6,2) DEFAULT NULL,
      `factor` decimal(4,2) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=TokuDB DEFAULT CHARSET=latin1
    
    CREATE TABLE `t2` (
      `id` int(15) NOT NULL AUTO_INCREMENT,
      `v_id` int(15) NOT NULL,
      `pid` int(11) DEFAULT NULL,
      `amount` decimal(6,2) DEFAULT NULL,
      `unit` int(1) DEFAULT '0',
      PRIMARY KEY (`id`)
    ) ENGINE=TokuDB DEFAULT CHARSET=latin1
    
      

    我知道我没有使用任何其他索引   主键索引。这是由于密钥对时间的负面影响   有插入。将在该处创建每个表的群集密钥   插入作业结束。

    其他MySQL命令行选项:

    SET unique_checks=OFF;
    

    不知怎的,我无法在my.cnf中得到这个...如果有人会知道怎么会非常感激(目前unique_checks = off会因为我的未知变量而阻止MySQL启动.CNF)。

    SQL语句按批次分组为15.000。 PHP脚本生成SQL语句并通过mysqli_multiquery将查询发送到MySQL服务器:

    <?PHP        
        foreach (generateCombinations($Arr) as $c) {
    
                $QueryBatch[] = "insert into t1 values (NULL"
                                . ", " . $record->id
                                . ", " . rand(1, 35)
                                . ", " . rand(1, 140)
                                . ", " . rand(1, 20)
                                . ", NULL"
                                . ", " . rand(1, 14)
                                . ", " . rand(1, 300)
                                . ", " . rand(1, 4)
                                . ", NULL );";
                $QueryBatch[] = "SET @t1id = LAST_INSERT_ID();";
    
                $cntBatch++;
    
                $pquery = array();
                foreach ( $c as $key => $pid){
    
                        if ( is_null($pid) )
                                continue;
    
                        $pquery[] = "(NULL, @t1id, " . $pid . ", " . rand(1, 800) . ", 0)";
    
                        $cntBatch++;
                }
    
                $QueryBatch[] = "insert into t2 values " . implode(',', $pquery) . ";";
    
                if ($cntBatch > 15000) {
    
                        $query = implode($QueryBatch);
    
                        if ( $mysqli->multi_query($query) ){
                                while ($mysqli->next_result()) {;}
                        } else {
                                printf("Errormessage: %s\n", $mysqli->error);
                                echo $query . "\n";
                        }
    
                        $cntBatch = 0;
                        unset($QueryBatch);
                }
    
        }
    ?>
    

    SQL insert语句的示例:

    insert into t1 values (NULL, 1 , 30, 100, 15, NULL, 10, 250, 2, NULL );
    SET @t1id = LAST_INSERT_ID();
    insert into t2 values (NULL, @t1id, 1, 750, 0),(NULL, @t1id, 1, 600, 0),(NULL, @t1id, 1, 500, 0),(NULL, @t1id, 1, 400, 0),(NULL, @t1id, 1, 300, 0),(NULL, @t1id, 1, 200, 0),(NULL, @t1id, 1, 100, 0);
    insert into t1 values (NULL, 2 , 25, 95, 14, NULL, 11, 200, 3, NULL );
    SET @t1id = LAST_INSERT_ID();
    insert into t2 values (NULL, @t1id, 1, 600, 0),(NULL, @t1id, 1, 100, 0),(NULL, @t1id, 1, 300, 0),(NULL, @t1id, 1, 443, 0),(NULL, @t1id, 1, 521, 0),(NULL, @t1id, 1, 213, 0),(NULL, @t1id, 1, 433, 0);
    [.. At least 14982 more..]
    

1 个答案:

答案 0 :(得分:2)

如果是我,我会减少正在执行的语句数量,并减少提交次数。我假设AUTO_COMMIT已启用,因为我们没有看到任何BEGIN TRANSACTIONCOMMIT语句。

这是一大堆个人INSERTSET陈述。至少子表的插入使用多行插入,而不是每行的单独插入语句。

如果我需要这么快,我会

  1. id表生成t1值,并在INSERT语句中包含这些值
  2. 取消对LAST_INSERT_ID()
  3. 的调用
  4. t1使用多行插入(而不是为每行分隔INSERT语句)
  5. 使用BEGIN TRANSACTIONCOMMIT
  6. 运行单个进程以执行插入t1(序列化)以避免潜在的锁定争用
  7. 如果是InnoDB,我也会SET FOREIGN_KEY_CHECKS=0

    在代码中已经有大量的rand函数调用;所以增加id的整数t1并不会移动指针。当我们开始时,我们需要一个查询来获取当前的AUTO_INCREMENT值,或者获取MAX(id),无论哪个......

    基本上,我减少了正在执行的语句数量,并且每个语句完成了更多工作,并且在每个COMMIT之前完成了更多工作。

    每个语句插入十(10)t1显着减少需要执行的语句数。

    BEGIN TRANSACTION;
    -- insert ten rows into t1
    INSERT INTO t1 (id,m_id,c1,c2,c3,c4,c5,c6,c7,factor) VALUES
     (444055501, 1 , 30, 100, 15, NULL, 10, 250, 2, NULL )
    ,(444055502, 2 , 25, 95, 14, NULL, 11, 200, 3, NULL )
    , ...
    ,(444055510, 10 , 7, 45, 12, NULL, 10, 300, 4, NULL )
    ;
    -- batch together the t2 rows associated with the ten t1 rows we just inserted
    INSERT INTO t2 VALUES
    -- 444055501  
     (NULL, 444055501, 1, 750, 0)
    ,(NULL, 444055501, 1, 600, 0)
    ,(NULL, 444055501, 1, 500, 0)
    ,(NULL, 444055501, 1, 400, 0)
    ,(NULL, 444055501, 1, 300, 0)
    ,(NULL, 444055501, 1, 200, 0)
    ,(NULL, 444055501, 1, 100, 0)
    -- 444055502  
    ,(NULL, 444055502, 1, 600, 0)
    ,(NULL, 444055502, 1, 100, 0)
    ,(NULL, 444055502, 1, 300, 0)
    ,(NULL, 444055502, 1, 443, 0)
    ,(NULL, 444055502, 1, 521, 0)
    ,(NULL, 444055502, 1, 213, 0)
    ,(NULL, 444055502, 1, 433, 0)
    -- 444055503
    , ...
    ;
    
    -- another ten rows into t1
    INSERT INTO t1 (id,m_id,c1,c2,c3,c4,c5,c6,c7,factor) VALUES
     (444055511, 11 , 27, 94, 15, NULL, 10, 250, 11, NULL )
    ,(444055512, 12 , 24, 93, 14, NULL, 11, 200, 12, NULL )
    , ...
    ,(444055520, 10 , 7, 45, 12, NULL, 10, 300, 4, NULL )
    ;
    INSERT INTO t2 VALUES
     (NULL, 444055511, 1, 820, 0)
    ,(NULL, 444055511, 1, 480, 0)
    , ...
    ;
    
    -- repeat INSERTs into t1 and t2, and after 1000 loops
    -- i.e. 10,000 t1 rows, do a commit
    COMMIT;
    BEGIN TRANSACTION;
    INSERT INTO t1 ...
    

    LOAD DATA INFILE

    如果没有提及LOAD DATA INFILE,任何关于插入性能的讨论都是不完整的。

    为了获得最佳性能,无法击败。但是,由于我们没有将数据存储在文件中,并且我们没有关键值(t2中的外键需要,而且我们已经获得了所有调用rand的权限要生成数据,LOAD DATA INFILE似乎不太适合。