主键乱序插入对Innodb性能的影响

在平时的mysql文档学习中我们经常会看到这么一句话

MySQL tries to leave space so that future inserts do not incur un-necessary page splits (and thus higher IO cost). In an "ideal" world, MySQL tries to keep the index pages at 15/16-th full, but depending on insert order, this fill factor can be as low as 1/2

大致含义就是当我们按照索引顺序插入时,page的填充率能达到15/16 , 而乱序插入时只能到略大于 1/2 的填充率。

那么这个说法是否正确呢?是否有相应的理论依据呢?

本文将通过一些测试来验证这个观点的真伪。

测试数据准备

简介: 顺序数据通过sysbench --oltp-table-size = 8000000 生成,然后通过order by rand() 生成乱序数据。

mysql> desc sbtest;
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| k     | int(10) unsigned | NO   | MUL | 0       |                |
| c     | char(120)        | NO   |     |         |                |
| pad   | char(60)         | NO   |     |         |                |
+-------+------------------+------+-----+---------+----------------+

  

#顺序文件<BR>mysql> select * from sbtest into outfile '/xfs/mysql3311/order.txt' ;<BR>#乱序文件
mysql> select b.* from rand_num a left join sbtest b on a.id = b.id into outfile '/xfs/mysql3311/random-order.txt';

  

文件load 性能测试

从结果可以很明显的看出 53sec vs 719sec,加载速度慢了12倍之多。

通过B-tree的原理我们也可以知道,乱序插入时Innodb需要不停的申请新的page,并且进行tree的重新分布,导致插入速度变慢。

CREATE TABLE `sbtest_order` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `k` int(10) unsigned NOT NULL DEFAULT '0',
  `c` char(120) NOT NULL DEFAULT '',
  `pad` char(60) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

mysql> load data infile '/xfs/mysql3311/order.txt' into table sbtest_order;
Query OK, 8000000 rows affected (53.23 sec)
Records: 8000000
Deleted: 0 Skipped: 0 Warnings: 0

 

  

CREATE TABLE `sbtest_rand` (
  `id` int(10) unsigned NOT
NULL AUTO_INCREMENT,
  `k` int(10) unsigned NOT
NULL DEFAULT '0',
  `c` char(120) NOT
NULL DEFAULT '',
  `pad` char(60) NOT
NULL DEFAULT '',
  PRIMARY
KEY
(`id`)
) ENGINE=InnoDB DEFAULT
CHARSET=utf8;

mysql> load data infile '/xfs/mysql3311/random-order.txt' into table
sbtest_random;

Query OK, 8000000 rows affected (11 min 59.09 sec)
Records: 8000000
Deleted: 0 Skipped: 0 Warnings: 0

  

页面填充率

通过xtrabackup的附带功能查看数据文件,可以知道page填充率。

从下文标红的数据可以看到:

  • 顺序的page填充率= 92% = 15/16
  • 乱序的page填充率= 65% = 10/16

基本和理论值一致

shell> xtrabackup --defaults-file=/usr/local/mysql3311/my.cnf --stats
--tables="sbtest[.]sbtest_order*"
--datadir=/xfs/mysql3311

<INDEX STATISTICS>
table: sbtest/sbtest_order, index: PRIMARY,
space id: 11, root page: 3, zip size: 0
estimated statistics in
dictionary:
key vals: 8000079, leaf pages: 109590, size pages: 109696
real
statistics:
level 2 pages: pages=1, data=1196 bytes, data/pages=7%
level 1
pages: pages=92,
data=1424670 bytes, data/pages=94%
leaf pages: recs=8000000, pages=109590,
data=1664000000 bytes, data/pages=92%

shell> xtrabackup --defaults-file=/usr/local/mysql3311/my.cnf --stats
--tables="sbtest[.]sbtest_random*"
--datadir=/xfs/mysql3311
<INDEX STATISTICS>
table:
sbtest/sbtest_random, index: PRIMARY, space id: 12, root page: 3, zip size:
0
estimated statistics in dictionary:
key vals: 8916256, leaf pages:
155403, size pages: 177920
real statistics:
level 2 pages: pages=1,
data=2899 bytes, data/pages=17%
level 1 pages: pages=223, data=2020239 bytes,
data/pages=55%
leaf pages: recs=8000000, pages=155403, data=1664000000 bytes,
data/pages=65%

查询性能测试:

使用8并发的sysbench进行OLTP测试,查看两种方式的性能差异。

  • TPQ:3078 vs 2803      约10%性能损耗
  • Res:2.75ms vs 3.85ms  约40%性能损耗
  • Data Size:1.8G vs 2.8G  1.5倍的空间损耗

sysbench --num-threads=8 --max-time=60 --max-requests=9999999 --test=oltp
--oltp-table-size=8000000 --mysql-socket=/xfs/mysql3311/mysql.sock
--mysql-user=root --mysql-password=password --mysql-table-engine=innodb --oltp-table-name=sbtest_order  
run

transactions: 184697 (3078.18 per
sec
.)
deadlocks: 0 (0.00 per sec.)
read/write requests:
3509243 (58485.34 per sec.)
other operations: 369394 (6156.35 per sec.)

approx. 95 percentile: 2.75ms

sysbench --num-threads=8 --max-time=60 --max-requests=9999999 --test=oltp
--oltp-table-size=8000000 --mysql-socket=/xfs/mysql3311/mysql.sock
--mysql-user=root --mysql-password=password --mysql-table-engine=innodb --oltp-table-name=sbtest_random  
run

transactions: 168213 (2803.44 per
sec.
)
deadlocks: 0 (0.00 per sec.)
read/write requests:
3196047 (53265.34 per sec.)
other operations: 336426 (5606.88 per sec.)

approx. 95 percentile:
3.85ms

总结

通过测试可以看出,按照主键的顺序插入可以带来10%的TPS提升,并能减少50%的空间浪费。

在平时的开发过程中,如果没有特别的业务需要,应该尽可能的使用自增列作为主键。

主键乱序插入对Innodb性能的影响的更多相关文章

  1. InnoDB的主键选择与插入优化

    索引的存放方式MyISAM和InnoDB存储引擎在MySQL中,不同存储引擎对索引的实现方式是不同的,总结下MyISAM和InnoDB两个存储引擎的索引实现方式.MyISAM引擎使用B+Tree作为索 ...

  2. SQLServer 自增主键创建, 指定自增主键列值插入数据,插入主键

    http://blog.csdn.net/zh2qiang/article/details/5323981 SQLServer 中含自增主键的表,通常不能直接指定ID值插入,可以采用以下方法插入. 1 ...

  3. Phoenix表和索引分区数对插入和查询性能的影响

    1. 概述 1.1 HBase概述 HBase由master节点和region server节点组成.在100-105集群上,100和101是master节点,102-105是region serve ...

  4. MyBatis魔法堂:Insert操作详解(返回主键、批量插入)

    一.前言    数据库操作怎能少了INSERT操作呢?下面记录MyBatis关于INSERT操作的笔记,以便日后查阅. 二. insert元素 属性详解   其属性如下: parameterType  ...

  5. MyBatis :Insert (返回主键、批量插入)

    一.前言    数据库操作怎能少了INSERT操作呢?下面记录MyBatis关于INSERT操作的笔记,以便日后查阅. 二.insert元素 属性详解   其属性如下: parameterType , ...

  6. MySQL数据表中有自增长主键时如何插入数据

    原文链接:https://blog.csdn.net/RuobaiMEN/article/details/79794199 MySQL数据库表中有自增主键ID,当用SQL插入语句中插入语句带有ID列值 ...

  7. Mysql EF 触发器生成主键id 存储区更新、插入或删除语句影响到了意外的行数(0)。实体在加载后可能被修改或删除。刷新 ObjectStateManager 项 ;System.Data.Entity.Infrastructure.DbUpdateConcurrencyException

    http://stackoverflow.com/questions/24725261/how-to-use-a-custom-identity-column-in-sql-with-entity-f ...

  8. 关于前台主键输入错误对后台hibernate方法的影响

    由于前台输入时开始不小心打错了主键为value=“${conf_id}”/ 导致后台得到的主键不是数字“1”而是“1/”所以到后台就算是进的updata方法结果运行的却是添加方法 原因可能是传入的对象 ...

  9. sql server中主键列的插入问题

    仅当使用了列列表并且 IDENTITY_INSERT 为 ON 时,才能为表'dbo.t_test'中的标识列指定显式值. SET IDENTITY_INSERT dbo.t_test ON ,'c' ...

随机推荐

  1. Android UI系列--对话框(一)(AlertDialog,TimePickerDialog,DatePickerDialog,ProgressDialog)

    一.Dialog介绍 dialog就是一个在屏幕上弹出一个可以让用户做出一个选择,或者输入额外的信息的对话框,一个对话框并不会沾满我们整个的屏幕,并且通常用于模型事件当中需要用户做出一个决定后才会继续 ...

  2. CSS 入门基础

    一.CSS 介绍什么是CSS CSS 指的是层叠样式表(Cascading StyleSheet).在网页制作时采用层叠样式表技术, 可以有效地对页面的布局.字体.颜色.背景和其它效果实现更加精确的控 ...

  3. Hadoop的safeMode

    当集群启动的时候,会首先进入到安全模式.系统在安全模式下,会检查数据块的完整性.假设我们设置的副本数(即参数dfs.replication)是5,那么在dataNode上就应该有5个副本存在,假设只存 ...

  4. 初次就这么给了你(Django-rest-framework)

    Django-Rest-Framework Django-Rest框架是构建Web API强大而灵活的工具包. 简单粗暴,直奔主题. pip install django pip install dj ...

  5. thinkphp的select和find的区别

    hinkphp是比较好的php开发框架,能比较快速的开发MVC架构的管理系统,我们需要用到 select()和find()方法,两个方法都能返回数据集数组,但有什么不同呢?先看一下我的代码对比:$te ...

  6. Android 实现UI设计

    1. 计算屏幕高度,宽度代码(Activity中) DisplayMetrics outMetrics = new DisplayMetrics(); getWindowManager().getDe ...

  7. 实例化vue之前赋值html元素导致事件失效

    先实例化Vue对象,再操作其他对象,Vue对象声明时会渲染html容器内的所有元素, 会导致元素事件失效或dom元素重新创建,所以涉及html元素的对象都要在实例化Vue之后执行. 下面是简要的例子, ...

  8. 虚拟机中ubuntu-16.04 Linux系统下配置mysql数据库,并在windows下使用navicat远程连接

    Linux系统下mysql数据库安装配置步骤: 1.在服务器上安装mysql:sudo apt-get install mysql-server sudo apt-get install mysql- ...

  9. JavaScript面向对象基础与this指向问题

      前  言           我们的程序语言经历了从"面向机器".到"面向过程".再到"面向对象"的一个过程.而JavaScript是一 ...

  10. DevOps之平台架构

    唠叨话 关于德语噢屁事的知识点,仅提供精华汇总,具体知识点细节,参考教程网址,如需帮助,请留言. DevOps平台架构(Platform Architecture) <虚拟化平台(Platfor ...