工做经历中,随着业务数据长时间积累,Mysql的数据也稍微有必定的量,因而乎当时咱们进行一次服务端慢查询大排查,肯定慢查询属于哪一个工程而且将其优化掉。我工程内也有一个,大致是MQ订阅到的taskid,taskid关联task的log表去找最近的一条记录的时间,而后根据时间校验是否放行作相应业务处理。我explain下,发现当时写的时候,log表的taskid也没有建索引,当log表的记录积累起来后,这个查询会显得很慢,建索引后有立竿见影的效果,固然这只是一个很是简单的场景。其实这里还衍生出一个问题:当log表数据量过大时候修改表结构,会形成一段时间的锁表。虽然有些方式能够避免锁表,可是“合理时机”建立索引仍是很重要的。知其然,要知其因此然,来看看索引的那些事儿。html
Mysql数据通常以文件的形式存储在磁盘上,读取数据时须要在磁盘上进行IO操做。当须要从磁盘读取数据时,系统会将数据逻辑地址传给磁盘,磁盘的控制电路按照寻址逻辑将逻辑地址翻译成物理地址,即肯定要读的数据在哪一个磁道,哪一个扇区。为了读取这个扇区的数据,须要将磁头放到这个扇区上方,为了实现这一点,磁头须要移动对准相应磁道,这个过程叫作寻道,所耗费时间叫作寻道时间,而后磁盘旋转将目标扇区旋转到磁头下,这个过程耗费的时间叫作旋转时间。mysql
当一个数据被用到时,其附近的数据也一般会立刻被使用。sql
因为磁盘顺序读取的效率很高(不须要寻道时间,只需不多的旋转时间),所以对于具备局部性的程序来讲,预读能够提升I/O效率。预读的长度通常为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操做系统每每将主存和磁盘存储区分割为连续的大小相等的块,每一个存储块称为一页(在许多操做系统中,页得大小一般为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,而后异常返回,程序继续运行。数据库
上边内容能够看出磁盘IO很是耗时,因此索引归根结的目的:减小耗时磁盘IO次数,提升获取数据的性能。缓存
在使用InnoDB存储引擎时,若是没有特别的须要,请永远使用一个与业务无关的自增字段做为主键。从数据库索引优化角度看,使用InnoDB引擎而不使用自增主键绝对是一个糟糕的主意。上文讨论过InnoDB的索引实现,InnoDB使用汇集索引,数据记录自己被存于主索引(一颗B+Tree)的叶子节点上。这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放,所以每当有一条新的记录插入时,MySQL会根据其主键将其插入适当的节点和位置,若是页面达到装载因子(InnoDB默认为15/16),则开辟一个新的页(节点)。若是表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。以下图所示:bash
假设当前联合索引为:KEY a_id_state_index
(a_id
,name
) 查询验证:性能
EXPLAIN SELECT * FROM `user` WHERE `a_id` = 5 AND `name` = 'cj_25'
EXPLAIN SELECT * FROM `user` WHERE `name` = 'cj_25' AND `a_id` = 5
复制代码
注:上面两句结果都以下图,由于mysql会对where里面的条件顺序在查询以前会被mysql自动优化优化
EXPLAIN SELECT * FROM `user` WHERE `a_id` = 5
复制代码
EXPLAIN SELECT * FROM `user` WHERE `name` = 'cj_25'
复制代码