1

目前我正在测试 TokuDB,我印象非常深刻。此时,每秒插入量已达到峰值,每秒超过 50.000 次,同时运行了两个作业。平均插入速率在每秒 38.000 到 42.000 次插入之间。

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

我目前的设置:

  1. 硬件:VPS,4GB RAM,150GB SSD,2 核:Intel Westmere E56xx/L56xx/X56xx (Nehalem-C) 2.59GHz CPU
  2. 磁盘挂载选项:默认值、noatime
  3. 操作系统:CentOS 6.8 64bit
  4. 数据库:Percona 服务器 5.7.14-8

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 启动,因为 my.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 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..]
4

1 回答 1

2

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

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

如果我需要这个速度很快,我会

  1. 生成表的idt1,并将这些值包含在 INSERT 语句中
  2. 取消调用LAST_INSERT_ID()
  3. 使用多行插入t1(而不是为每一行单独插入语句)
  4. 使用BEGIN TRANSACTIONCOMMIT
  5. 运行单个进程以执行插入t1(序列化)以避免潜在的锁争用

如果是 InnoDB,我也会这样做SET FOREIGN_KEY_CHECKS=0

代码中已经有大量对该rand函数的调用;所以增加一个整数idfort1不会移动指针。当我们开始时,我们需要一个查询来获取当前的 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

为了获得最佳性能,这是无法击败的。但是由于我们没有文件中的数据,并且我们没有键值(在 中需要外键t2,并且我们已经对 rand 进行了所有调用来生成数据,LOAD DATA INFILE所以似乎不是很合身。

于 2016-10-07T22:53:40.830 回答