在此还记录了一些mysql基础知识,可以用作参考

优化方案

插入语句

普通使用的插入语句:
insert into table (col1,col2,col3) values (’ 1’,’ John ‘,’ stu ‘);
insert into table (col1,col2,col3) values (’ 2’,’ Jan ‘,’ stu ‘);
insert into table (col1,col2,col3) values (’ 3 ‘,’ Billy ‘,’ stu ');

插入操作时间分配:

链接耗时 (30%)
发送query到服务器 (20%)
解析query (20%)
插入操作 (10% * 词条数目)
插入index (10% * Index的数目)
关闭链接 (10%)

从这里可以看出单条语句,会在链接、解析部分耗费大量时间,因此我们大多时候会采用批量插入来提升插入速度,争取在一次链接中尽可能多的写入数据(下面通过几个参数来说明一次到底插入多少数据量合适)。

使用语句拼接

insert into table (col1,col2,col3) values (’ 1’,’ John ‘,’ stu ‘),(’ 2’,’ Jan ‘,’ stu ‘),(’ 3 ‘,’ Billy ‘,’ stu ');
使用拼接sql语句的优势:
降低了日志(MYSQL的binlog和innodb的事务日志)刷盘的数据量和频率、较少了SQL语句的解析次数、减少了网络传输IO(远程客户端插入数据)等;

对于拼接语句sql有一个长度限制(官方解释是适当增大 max_allowed_packet 参数可以使client端到server端传递大数据时,系统能够分配更多的扩展内存来处理。):
查看限制最大值:show variables like ‘%max_allowed_packet%’;

mysql> show variables like '%max_allowed_packet%';
+--------------------------+------------+
| Variable_name            | Value      |
+--------------------------+------------+
| max_allowed_packet       | 33554432   |
| slave_max_allowed_packet | 1073741824 |
+--------------------------+------------+
2 rows in set (0.00 sec)

设置限制最大值:mysql一般可以在配置文件中通过设置变量max_allowed_packet 的值来更改sql长度限制最大值;
在使用python的pymysql包提供的executemany函数,默认使用sql拼接插入数据;

设置完限制值后,那么我们在使用拼接时,一次性拼接多少条语句合适呢?首先我们需要计算出一条语句大概占用多少内存,然后通过我们设置的max_allowed_packet的50%-75%去除单条语句的大小,最后计算出每次拼接的数量;(这里的被除数设置为max_allowed_packet的50%-75%,是本人在自己测试时,得到效率最大的区间,每台机器可能不一致,个人猜想与内存等硬件相关);总之我们应该遵循一句话:减小通信间数据包的大小和数量是一个非常好的习惯。

使用事务

在一条insert语句中默认会开启一个事务,当执行多个insert语句时,事务的开销就会影响到插入数据的性能;因此最常用的优化手段就是在执行一块插入语句前手动开启事务(START TRANSACTION),执行完插入语句后使用COMMIT提交;
事务也需要控制大小,事务太大可能会影响执行的效率;MySQL有innodb_log_buffer_size配置项,超过这个值会把innodb的数据刷到磁盘中,这时,效率会有所下降。所以比较好的做法是,在数据达到这个这个值前进行事务提交。

innodb_log_buffer_size大小查询:

mysql> show variables like '%innodb_log_buffer_size%';
+------------------------+----------+
| Variable_name          | Value    |
+------------------------+----------+
| innodb_log_buffer_size | 67108864 |
+------------------------+----------+

总结:从以上来看,在我们提交插入数据时主要受到两个限制,一个是sql语句本身的大小,其次是事务大小限制;在最大限制下,我们可以开启事务使用sql拼接批量插入,可以节省事务开销,但是需要注意,内存是有限且共享的,如果批量插入占用太多的事务内存,那么势必会对其他的业务操作等有一定的影响。

注:事务和sql拼接从宏观上是差不多的,都是使用了类似缓存的原理,将整块数据一起处理,微观上处理的方式还是有本质区别的;

个人猜测:通过开启事务,会将插入的数据临时放到缓冲池中,等到COMMIT提交时,再落盘到磁盘中;当使用拼接sql
时,其实也是将大量数据放到缓冲池中,单个拼接后sql执行过程内也会建立事务,当完成后落盘;因此使用拼接和使用事务,或者同时使用其实都是差不多,只是使用时的参数设置不同;比如单独使用事务时innodb_log_buffer_size设置大点,单数使用拼接max_allowed_packet设置大点,两者同时使用时两个配置值都适量设置;当然目前只是猜测,等日后研究后再来完善,或者大佬们提点下。

数据插入影响

索引的影响

数据库插入时,需要维护索引的数据,当插入的记录无序时会增大维护索引(聚簇)的成本;我们可以参照innodb使用的B+tree索引,如果每次插入记录都在索引的最后面,索引的定位效率很高,并且对索引调整较小;如果插入的记录在索引中间,需要B+tree进行分裂合并等处理,会消耗比较多计算资源,并且插入记录的索引定位效率会下降,数据量较大时会有频繁的磁盘操作(无序,且缓冲池不够用)。

因此有序插入在一定程度上可以优化插入的效率。

插入缓存的影响(存在非聚簇索引时)

对于innodb引擎来说,一次插入是涉及到事务和锁的,所以插入并不能仅仅只考虑max_allowed_packet的问题,也要考虑服务器缓冲池的大小;
在innodb中存在一个插入缓存(insert buffer)的概念,所以在插入的时候也是要耗费一定的缓冲池内存的。当写密集的情况下,插入缓冲会占用过多的缓冲池内存,默认最大可以占用到1/2的缓冲池内存,当插入缓冲占用太多缓冲池内存的情况下,会影响到其他的操作。

缓冲池的大小查询:

mysql> show variables like 'innodb_buffer_pool_size';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 134217728 |
+-------------------------+-----------+

插入缓冲的详细解释:
我们都知道,在InnoDB引擎上进行插入操作时,一般需要按照主键顺序进行插入,这样才能获得较高的插入性能。当一张表中存在非聚簇的且不唯一的索引时,在插入时,数据页的存放还是按照主键进行顺序存放,但是对于非聚簇索引叶节点的插入不再是顺序的了,这时就需要离散的访问非聚簇索引页,由于随机读取的存在导致插入操作性能下降。
 InnoDB为此设计了Insert Buffer来进行插入优化。对于非聚簇索引的插入或者更新操作,不是每一次都直接插入到索引页中,而是先判断插入的非聚集索引是否在缓冲池中,若在,则直接插入;若不在,则先放入到一个Insert Buffer中。看似数据库这个非聚集的索引已经查到叶节点,而实际没有,这时存放在另外一个位置。然后再以一定的频率和情况进行Insert Buffer和非聚簇索引页子节点的合并操作。这时通常能够将多个插入合并到一个操作中,这样就大大提高了对于非聚簇索引的插入性能。

具体可以参考:https://cloud.tencent.com/developer/article/1200824
缓存池可以大致类比cpu将物理内存当作磁盘的缓存,innodb将缓存池用作磁盘的缓存

日志的影响

这里主要是介绍二进制日志BinLog:
MySQL 的二进制日志 binlog 可以说是 MySQL 最重要的日志,它记录了所有的 DDL 和 DML 语句(除了数据查询语句select、show等),以事件形式记录,还包含语句所执行的消耗的时间,MySQL的二进制日志是事务安全型的。binlog 的主要目的是复制和恢复,比如MySQL主从复制、数据恢复等;

启动或关闭binlog:通过配置文件中的log-bin配置项来启动及关闭;
查看是否启动了binlog:

mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin       | ON    |
+---------------+-------+
1 row in set (0.00 sec)

根据项目需求,可以选择关闭binlog来优化插入数据的效率。

服务器配置参数

mysql配置文件:my.cnf(linux)、my.ini(win);

  • innodb_buffer_pool_size(缓冲池)
    这个参数主要缓存innodb表的索引,数据,插入数据时的缓冲;
  • innodb_log_file_size
    指定日志文件大小;这个值分配的大小和数据库的写入速度,事务大小,异常重启后的恢复有很大的关系;
    当一个日志文件写满后,innodb会自动切换到另一个日志文件,而且会触发数据库检查点,这会导致缓存脏页小批量刷新,会明显降低innodb性能;
  • innodb_log_buffer_size
    事务在内存中的缓冲大小,我也看到说这是将日志写入磁盘日志文件前的缓冲大小;不过确实会影响到事务;
    一个事务能否成功提交的关键是日志是否成功落盘,与数据没有太大的关系
  • max_allowed_packet
    最大的sql语句大小限制;
    想要了解具体的参数可以参考:https://blog.csdn.net/wjc19911118/article/details/51784783

mysql常用的引擎:InnoDB(上面的介绍都是以该引擎为基础)、Myisam

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐