Mysql如何选择唯一索引和普通索引
相信大家对唯一索引和普通索引是有一定的了解的,那么在不同的业务场景,使用唯一索引还是普通索引呢,比如下面的场景
假设你在维护一个账户系统,每一个人都有一个唯一的身份证,而业务也能保证他的唯一性,此时我们设置唯一索引和普通索引其实都是可以的,但是在性能上他们有什么差异吗
select name from user where id_card='6102222xxxxxx'
那么我如何分析性能问题呢,我针对查询和更新两方面进行分析
查询过程
假设插叙的语句是select id from T where k=5,这个查询语句在索引树上查询的过程,先是通过B+树树根查询,找到叶子节点,也就是上图的右下角的数据页,然后通过数据页内容通过二分法定位记录
- 对于普通索引,查询到满足的而第一个记录后,需要向后继续寻找,直到不满足条件
- 对于唯一索引,由于索引的唯一性,查询到数据后,直接停止查询
上面的不同索引带来的性能差异是微乎其微的
一般InnoDB的数据是按数据页为单位来读写的,也就是说,当需要读取一条记录的时候,并不是把这个记录从磁盘读取出来,而是以页为单位整体的读入内存,innoDB的页的默认大小是16k.
由于是按照页的方式读取数据的,当k=5的时候,且数据页在内存中,只需要在内存中获取就可以了,对于普通索引,要多做一次"查询判断"操作,只需要一次指针寻找和一次计算,
但是如果内存中的数据页不包k=5的数据需要获取下一个数据页,这就比较复杂了.一个数据也一般可以存储上千个key,这种的概率也比较小,一般是可以忽略不记的。
更新过程
在说明更新过程之前,我们必须知道一个概念那就是change buffer.'
当需要更新一个数据页时,如果数据页在内存中,就直接把更新操作缓存在change buffer中,这样就不需要从磁盘读取这个数据页了,在下次查询的时候,将数据也从磁盘读取到内存中,然后执行change buffer和这个也有关的操作,通过这种方式保障数据的正确性
需要说明的是,虽然名字叫做change buffer,实际上他是可以持久化的数据,也就说,change buffer在内存中有拷贝,也是会写入磁盘的,将change buffer 中的操作应用到原数据页,得到最新的结果的过程叫做merge,除了这个数据页触发merge外,系统的后台定期会merge,在数据库正常关闭的时候,也会执行merge操作。
那么什么时候可以使用change buffer呢
对于唯一索引,所有的更新操作都要判断这个操作是否违反唯一性约束,比如要插入(4,400)记录,就要判断表中是否已经存在k=4的记录,而这将必须把数据也读取到内存中,既然已经在内存中了,我们直接更新内存会更快,就没有必要使用change buffer了,因此唯一索引的更新是不会使用change buffer,只有普通索引可以使用.
change buffer 使用的buffer pool的内存大小,因此不能无限增大,可以使用innodb_change_buffer_size来动态设置,这个参数设置为50表示change buffer最大使用buffer pool的50%。
以上就是change buffer基本原理,现在我们看看更新操作,插入(4,400)记录,分为两种情况
第一种是更新的记录在内存中
- 对于唯一索引找到3和5之间的位置,判断没有冲突就插入这个值,语句结束
- 对于普通索引找到3和5之间的位置,插入这个值,语句结束
这种情况差距就是判断冲突的操作,影响差别不大
第二种更新记录不在内存中
- 对于唯一索引,需要将数据页读入内存中,判断有没有冲突,插入这个值,语句结束
- 对于普通索引,仅仅把更新操作记录在change buffer中,语句结束
减少从磁盘读入内存以及随机IO访问,是数据库性能提高的操作之一,而change buffer就是在较少随机访问磁盘的操作,因此对性能的提高是很明显的。
change buffer使用场景
从上面分析我可以知道cahnge buffer对于唯一索引不起作用,只能使用在普通中,问题是普通索引中一定会起到加速作用吗
因为merge的时候是整整进行数据更新的时候,而change buffer主要目的就是记录更新操作,所以在一次merge之前,change buffer 的记录越大,起到的作用就越好,因此,对于写多读少的业务场景,使用change buffer的效果是最好的,
反过来,如果在更新完成之后,立马就进行查询,那么即使满足了条件,把更新的操作记录在change buffer中,但是随后就会进行查询,导致merge操作,这样随机访问IO次数不会减少,反而增加了change buffer的维护成本,做一这种业务场景,change buffer反而起到副作用。
索引的选择和实践
普通索引和唯一索引选择,其实,这类索引在查询能力上是没有差别,主要考虑的是对更新性能的影响,所以建议选择普通索引。
而在更新操作之后,马上就会执行查询操作,因此此时应该关闭change buffer,而在其他情况下,change buffer都能提高性能.
change buffer和redo log
WAL提高性能的核心机制,也的确是尽量减少随机读写,redo log 和change buffer 都有可以提高性能的作用,往往比较容易混淆.
如下面插入语句
insert into t(id,k) values(id1,k1),(id2,k2);
这里,我们假设当前k索引树的状态,查找到位置后,K1所在的数据页在内存中,k2所在的数据页不在内存中.
分析这条更新语句,你会发现他涉及四部分:内存,redo log ,数据表空间,系统表空间
这条更新语句做了如下操作
- Page1在内存中,直接更新内存
- Page2没有在内存中,就直接在内存中的change bufffer区域,记录下想page2插入这一行的信息
- 将上述两个动作计入redo log中
上面操作完,事物也就结束,索引你会看到,执行这条语句成本很低,就是写了两处内存,然后写入一下磁盘(两次操作合在一起操作写入磁盘),而且是顺序写的。
此时如果,我们要进行select * from t where k in(k1,k2),如果读语句发生在更新语句后不久,内存中的数据还存在,那么此时的这两个读操作就与系统表空间,和redo log无关了。而此时他们的顺序
- 读取page1的时候,直接从内存返回。而不需要读取磁盘,不需要把redo log的数据更新之后才可以返回,其实直接从内存中获取的数据就是正确的
- 要读取page2的时候,需要把page2从磁盘读入内存中,然后应用change buffer 里面的操作日志,生成一个正确的版本并返回结果。
可以看到,直到需要读page2的时候,这个数据页才会被读入内存。
所以,如果简单的对比这两个机制(change buffer,redo log)在提高性能上的收益的话,redo log主要节省的是随机写磁盘的IO消耗,而change buffer主要节省的则是随机读磁盘的IO消耗。
- 人们可能会犯的7个数据错误
- java:POI导出excel
- WordPress自定义栏目运用实例III:添加原创/转载文章不同版权声明
- 另一个强大的Visualizers :Mole For Visual Studio
- WordPress自定义栏目运用实例V:为加密文章添加密码提示文字
- java基础:所有参数皆是按值参数
- 使用Hystrix提高系统可用性
- Spring Security笔记:解决CsrfFilter与Rest服务Post方式的矛盾
- GitHub新开放项目FoolNLTK:一个便捷的中文处理工具包
- hessian学习
- 制作WordPress侧边栏“博客统计”小工具并集成在主题中的方法
- Struts2、Spring MVC4 框架下的ajax统一异常处理
- 前11月网游收入1341亿元同比增超两成 你贡献了多少?
- struts2: config-browser-plugin 与 convention-plugin 学习
- MySQL 教程
- MySQL 安装
- MySQL 管理与配置
- MySQL PHP 语法
- MySQL 连接
- MySQL 创建数据库
- MySQL 删除数据库
- MySQL 选择数据库
- MySQL 数据类型
- MySQL 创建数据表
- MySQL 删除数据表
- MySQL 插入数据
- MySQL 查询数据
- MySQL where 子句
- MySQL UPDATE 查询
- MySQL DELETE 语句
- MySQL LIKE 子句
- mysql order by
- Mysql Join的使用
- MySQL NULL 值处理
- MySQL 正则表达式
- MySQL 事务
- MySQL ALTER命令
- MySQL 索引
- MySQL 临时表
- MySQL 复制表
- 查看MySQL 元数据
- MySQL 序列 AUTO_INCREMENT
- MySQL 处理重复数据
- MySQL 及 SQL 注入
- MySQL 导出数据
- MySQL 导入数据
- MYSQL 函数大全
- MySQL Group By 实例讲解
- MySQL Max()函数实例讲解
- mysql count函数实例
- MYSQL UNION和UNION ALL实例
- MySQL IN 用法
- MySQL between and 实例讲解