MySQL提高批量insert的性能

Posted jerry-89

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL提高批量insert的性能相关的知识,希望对你有一定的参考价值。

一. 使用批量插入,将多条单独的 insert 合并成一次操作

即:insert into table values (a1, b1, c1), (a2, b2, c2);

解析:将多条 insert 合并后,减少mysql日志量(即MySQL的 binlog 和 innodb 的事务日志),降低日志刷盘的数据量和频率,从而提高效率。通过合并多条 insert 语句,还能减少SQL语句的解析次数,减少和数据库的交互,减少了网络传输的IO。

二. 修改批量插入缓冲区大小参数: bulk_insert_buffer_size,调大批量插入的缓存

这个参数默认为 8M,例如设置成 100M

set global bulk_insert_buffer_size = 1024*1024*100;

三. 设置 innodb_flush_log_at_trx_commit = 0

相对于 innodb_flush_log_at_trx_commit = 1, 设置为 0 可以明显的提高导入的速度。

set global innodb_flush_log_at_trx_commit=0;

解析:

        0: log buffer 中的数据将以每秒一次的频率写入到 log file 中,且同时会进行文件系统到磁盘的同步操作,但是每个事务的 commit 并不会触发任何的 log buffer 到 log file 的刷新或者从文件系统到磁盘的刷新操作。

        1: 在每次事务提交的时候将 log buffer 中的数据都会写入到 log file,同时也会触发文件系统到磁盘的同步;

        2: 事务提交会触发 log buffer 到 log file 的刷新,但并不会触发磁盘文件系统到磁盘的同步。此外,每秒会有一次文件系统到磁盘的同步操作。

四. 设置事务提交为 手动提交

因为 MySQL 事务默认是自动提交的(autocommit),这样每插入一条数据,都会进行一次 commit,所以通过手动提交事务,可以减少创建事务时的消耗。一般可以设置为 1000 条insert 提交一次。
 【解决方案1】:

通过在 my.cnf中添加以下行。我能够做到。

innodb_autoinc_lock_mode =2
sync_binlog=1
bulk_insert_buffer_size=512M
key_buffer_size=512M
read_buffer = 50M
Max_allowed_packet=1M
Net_buffer_length=8k

innodb_flush_log_at_trx_commit=2,我在另一个链接中看到它说它将速度提高到 160 倍。 输出性能:超过 24 小时到 2 小时;

通常将innodb_buffer_pool_size其配置为物理内存的50%到75%

innodb_flush_log_at_trx_commit,1改为了0

2.尝试修改参数加快导入速度

在MySQL中,有一对大名鼎鼎的“双一”参数,即 innodb_flush_log_at_trx_commit 与 sync_binlog 。为了安全性这两个参数默认值为1,为了快速导入脚本,我们可以临时修改下这两个参数,下面简单介绍下这两个参数:

innodb_flush_log_at_trx_commit默认值为1,可设置为0、1、2

如果innodb_flush_log_at_trx_commit设置为0,log buffer将每秒一次地写入log file中,并且log file的flush(刷到磁盘)操作同时进行.该模式下,在事务提交的时候,不会主动触发写入磁盘的操作。 如果innodb_flush_log_at_trx_commit设置为1,每次事务提交时MySQL都会把log buffer的数据写入log file,并且flush(刷到磁盘)中去. 如果innodb_flush_log_at_trx_commit设置为2,每次事务提交时MySQL都会把log buffer的数据写入log file.但是flush(刷到磁盘)操作并不会同时进行。该模式下,MySQL会每秒执行一次 flush(刷到磁盘)操作。

sync_binlog默认值为1,可设置为[0,N)

当sync_binlog =0,像操作系统刷其他文件的机制一样,MySQL不会同步到磁盘中去而是依赖操作系统来刷新binary log。 当sync_binlog =N (N>0) ,MySQL 在每写 N次 二进制日志binary log时,会使用fdatasync()函数将它的写二进制日志binary log同步到磁盘中去。

这两个参数可以在线修改,若想快速导入,可以按照下面步骤来操作:

# 1.进入MySQL命令行 临时修改这两个参数setglobalinnodb_flush_log_at_trx_commit = 2;setglobalsync_binlog = 2000;# 2.执行SQL脚本导入mysql -uroot -pxxxxxx testdb < testdb.sql# 3.导入完成 再把参数改回来setglobalinnodb_flush_log_at_trx_commit = 1;setglobalsync_binlog = 1;

还有另外一种场景是你的需求是新建从库,或者是不需要产生binlog,这时候导入SQL脚本时可以设置暂时不记录binlog,可以在脚本开头增加 set sql_log_bin=0; 然后再执行导入,这样速度会进一步加快。如果你的MySQL实例没有开启binlog则不需要再执行该语句了。

max_connection=1000

innodb_file_per_table=1

innodb_buffer_pool_size=1G

innodb_log_file_size=256M

innodb_log_buffer_size=256M

innodb_flush_log_at_trx_commit=2 需要放mysqlld_safe节点下。

symbolic-links=0
innodb_lru_scan_depth=256
#sync_binlog=0
#innodb_flush_log_at_trx_commit=0
wait_timeout=2880000
#interactive_time=2880000
max_allowed_packet=1G
max_connections = 1000
innodb_file_per_table=1
innodb_buffer_pool_size=10G
innodb_log_file_size=256M
innodb_log_buffer_size=256M
bulk_insert_buffer_size = 1G
innodb_flush_log_at_trx_commit=0
innodb_autoinc_lock_mode =2
sync_binlog=0
bulk_insert_buffer_size=512M
key_buffer_size=512M
#read_buffer=50M
net_buffer_length=8k
character_set_server = utf8mb4
sql_mode = NO_ENGINE_SUBSTITUTION

MySQL大数据量插入数据参数优化

 关于一些配置项优化
[client]
port = 3306
socket = /var/lib/mysql/mysql.sock

[mysql.server]
pid-file = /var/lib/mysql/localhost.pid

[mysqld]
port = 3306
socket =/var/lib/mysql/mysql.sock

# 网络传输中一次消息传输量的最大值。系统默认值 为1MB,最大值是1GB,必须设置1024的倍数。
# 因为后续我们在代码中是使用prepareStatement.addBatch(),单次传输的值会比较大,所以这个参数必须加大。
max_allowed_packet = 32M
# 缓冲池大小,只需要用Innodb的话则可以设置它高达 70-80% 的可用内存。
innodb_buffer_pool_size = 5120M
innodb_buffer_pool_instances=3
innodb_additional_mem_pool_size = 1024M
innodb_log_files_in_group = 3
innodb_lock_wait_timeout = 120
# 刷新日志频率
open_files_limit = 10240
back_log = 600
innodb_log_file_size=110M
# 连接数
max_connections = 3000
max_connect_errors = 6000
# 多线程
thread_cache_size = 300
thread_concurrency = 16
thread_stack = 192KB
innodb_write_io_threads = 32
innodb_purge_threads=1
# 刷新脏页频率
innodb_max_dirty_pages_pct=90
wait_timeout=300

详细的参数配置
innodb缓冲池相关配置


MySQL性能优化

MySQL批量SQL插入性能优化

对于一些数据量较大的系统,数据库面临的问题除了查询效率低下,还有就是数据入库时间长。特别像报表系统,每天花费在数据导入上的时间可能会长达几个小时或十几个小时之久。因此,优化数据库插入性能是很有意义的。
经过对MySQL innodb的一些性能测试,发现一些可以提高insert效率的方法,供大家参考参考。

1. 一条SQL语句插入多条数据。
常用的插入语句如:

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES (\'0\', \'userid_0\', \'content_0\', 0);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES (\'1\', \'userid_1\', \'content_1\', 1);

修改成:

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES (\'0\', \'userid_0\', \'content_0\', 0), (\'1\', \'userid_1\', \'content_1\', 1);

修改后的插入操作能够提高程序的插入效率。这里第二种SQL执行效率高的主要原因是合并后日志量(MySQL的binlog和innodb的事务让日志)减少了,降低日志刷盘的数据量和频率,从而提高效率。通过合并SQL语句,同时也能减少SQL语句解析的次数,减少网络传输的IO。
这里提供一些测试对比数据,分别是进行单条数据的导入与转化成一条SQL语句进行导入,分别测试1百、1千、1万条数据记录。

2. 在事务中进行插入处理。
把插入修改成:

START TRANSACTION;
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES (\'0\', \'userid_0\', \'content_0\', 0);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES (\'1\', \'userid_1\', \'content_1\', 1);
...
COMMIT;

使用事务可以提高数据的插入效率,这是因为进行一个INSERT操作时,MySQL内部会建立一个事务,在事务内才进行真正插入处理操作。通过使用事务可以减少创建事务的消耗,所有插入都在执行后才进行提交操作。
这里也提供了测试对比,分别是不使用事务与使用事务在记录数为1百、1千、1万的情况。

3. 数据有序插入。
数据有序的插入是指插入记录在主键上是有序排列,例如datetime是记录的主键:

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES (\'1\', \'userid_1\', \'content_1\', 1);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES (\'0\', \'userid_0\', \'content_0\', 0);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES (\'2\', \'userid_2\', \'content_2\',2);

修改成:

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES (\'0\', \'userid_0\', \'content_0\', 0);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES (\'1\', \'userid_1\', \'content_1\', 1);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES (\'2\', \'userid_2\', \'content_2\',2);

由于数据库插入时,需要维护索引数据,无序的记录会增大维护索引的成本。我们可以参照innodb使用的B+tree索引,如果每次插入记录都在索引的最后面,索引的定位效率很高,并且对索引调整较小;如果插入的记录在索引中间,需要B+tree进行分裂合并等处理,会消耗比较多计算资源,并且插入记录的索引定位效率会下降,数据量较大时会有频繁的磁盘操作。
下面提供随机数据与顺序数据的性能对比,分别是记录为1百、1千、1万、10万、100万。

从测试结果来看,该优化方法的性能有所提高,但是提高并不是很明显。

 

性能综合测试:
这里提供了同时使用上面三种方法进行INSERT效率优化的测试。

从测试结果可以看到,合并数据+事务的方法在较小数据量时,性能提高是很明显的,数据量较大时(1千万以上),性能会急剧下降,这是由于此时数据量超过了innodb_buffer的容量,每次定位索引涉及较多的磁盘读写操作,性能下降较快。而使用合并数据+事务+有序数据的方式在数据量达到千万级以上表现依旧是良好,在数据量较大时,有序数据索引定位较为方便,不需要频繁对磁盘进行读写操作,所以可以维持较高的性能。

 

注意事项:
1. SQL语句是有长度限制,在进行数据合并在同一SQL中务必不能超过SQL长度限制,通过max_allowed_packet配置可以修改,默认是1M,测试时修改为8M。
2. 事务需要控制大小,事务太大可能会影响执行的效率。MySQL有innodb_log_buffer_size配置项,超过这个值会把innodb的数据刷到磁盘中,这时,效率会有所下降。所以比较好的做法是,在数据达到这个这个值前进行事务提交。

 

以上是关于MySQL提高批量insert的性能的主要内容,如果未能解决你的问题,请参考以下文章

MySQL批量SQL插入性能优化

MySQL批量SQL插入性能优化详解

MySQL批量SQL插入各种性能优化

MySQL批量SQL插入性能优化

Oracle数据的批量插入

mysql批量插入数据