本篇文章主要介绍了"索引原理与表设计",主要涉及到方面的内容,对于其他数据库感兴趣的同学可以参考一下:
作者:黄湘龙架构师交流群(312254004)欢迎非商业转载,商业使用请联系我索引是有效使用数据库的基础,但你的数据量很小的时候,或许通过扫描整表来存取数据的性...
select * from PW_Like where userId = 80000402;
# Query_time: 0.002892 Lock_time: 0.000062 Rows_sent: 27 Rows_examined: 27 Rows_affected: 0
# Bytes_sent: 399 Tmp_tables: 0 Tmp_disk_tables: 0 Tmp_table_sizes: 0
# QC_Hit: No Full_scan: No Full_join: No Tmp_table: No Tmp_table_on_disk: No
# InnoDB_pages_distinct: 15
我们可以看到的结果是,虽然我们只取出了27条数据,但是我们读取了15个数据Page,花了2.8毫秒,虽然没有进行全局扫描,但基本上也把一般的数据块读取出来了。因为PW_Like中的数据由于是按照feedId物理排序的,所以这27条数据分别分布在13个Page中(有两个Page是索引和主键),所以数据库需要把这13个Page全部从磁盘中读取出来,哪怕某一个Page(16K)上只有一条数据(15Bytes),也需要把这个数据Page读出来才能取出所有的目标Row。
通过普通索引来检索批量数据,效率明显比通过主键来检索要低得多。因为数据是分散的,所以需要分散地读取数据Page进行拼接才能完成任务。但是索引对主键而言还是非常有必要的补充,比如上面这个例子,当用户量达到100万的时候,检索某一个用户点的所有的赞的成本也只是大概读取15个Page,花2ms左右。
3.2检索一段时间范围内的数据
选取一定范围内的数据是我们经常要遇到的问题。在海量数据的表中检索一定范围内的数据,很容易引起性能问题。我们遇到的最常见的需求有以下几种:
- 选取一段时间内注册的用户信息
这种时候,时间肯定不会是用户表的主键,如果直接用时间作为选择条件来检索,效率会非常差,如何解决这种问题呢?我采取的办法是,把注册时间作为用户表的索引,每次先把需要检索的时间的两端的userId都差出来,然后用这两个userId做选择条件做第三次查询就是我们想要的数据了。这三次检索我们只需要读取大约10个Page就能解决问题。这种方法看起来很麻烦,但是是表的数据量达到亿级的时候的唯一解决方案。 - 选取一段时间内的日志
日志表是我们最常见的表,如何设计好是经常聊到的话题。日志表之所以不好设计是因为大家都希望用时间作为主键,这样检索一段时间内的日志将非常方便。但是用时间作为主键有个非常大的弊端,当日志插入速度很快的时候,就会出现因为主键重复而引起冲突。
对于这种情况,我一般把日志生成时间和一个自增的Id作为日志表的联合主键,把时间作为第一个字段,这样即避免了日志插入过快引起的主键唯一性冲突,又能便捷地根据时间做检索工作,非常方便。下面是这种日志表的检索的例子,大家可以看到性能非常好。还有就是日志表最好是每天一个表,这样能更便利地管理和检索。
select * from log_test where logTime > "2015-12-27 11:53:05" and logTime < "2015-12-27 12:03:05";
# Query_time: 0.001158 Lock_time: 0.000084 Rows_sent: 599 Rows_examined: 599 Rows_affected: 0
# Bytes_sent: 4347 Tmp_tables: 0 Tmp_disk_tables: 0 Tmp_table_sizes: 0
# QC_Hit: No Full_scan: No Full_join: No Tmp_table: No Tmp_table_on_disk: No
# InnoDB_pages_distinct: 3
4.批量选择的效率
我们在批量检索数据的时候,对选择的结果在大多数情况下,都需要数据是排好序的。排序的性能也是日常需要注意到的,下面我们分三种情况来分析下数据库是如何排序的。
对于ORDER BY 主键这种情况,数据库是非常乐于见到的,基本上不会有额外性能的损耗,因为数据本来就是按照主键顺序存储的,取出来直接返回即可。
下面的所有关于排序的例子是在UP_MessageHistory表上做的实验,这个表一共有195个page,35417行数据,主键建立在字段id上,在sendUserId和destUserId上都建立了索引。
首先我们先做一个没有排序的检索: