数据库的设计与调优
主键策略
先说结果,项目最后采用了BIGINT类型的自增Id方式。
-
思路:
考虑到对事务的支持和主键索引效率(聚集索引),采用默认的InnoDB引擎。在InnoDB下,主键选用有以下几个原则:
-
主键最好是单调(递增)的。
因为主键默认是聚集索引,而聚集索引的叶子节点存放的就是数据本身(而非地址),索引的键值逻辑顺序决定了表数据行的物理存储顺序,也就是说,如果索引是递增的,则该索引在磁盘上的物理存储地址是也相邻的。我们都知道索引是存在磁盘而非内存中的,磁盘有一个预读机制,当一个数据被读到时,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存,从而减少磁盘IO,而聚集索引的设计就恰恰利用了这一点,利用更少的磁盘IO读取更多的数据。假设我们用非单调的字段作为主键,当我们要进行范围查询(between、<、<=、>、>=)时,磁盘要不停的寻址,效率大大下降;另外在插入新记录时,数据文件为了维持B+Tree的特性会频繁的分裂调整,十分低效。
-
主键不宜过长。
接上一点,聚集索引使得按主键检索十分高效,但是按辅助索引检索就需要先检索辅助索引获得主键,然后用主键到主索引中检索获得记录,因此所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。PS:每个非聚簇索引都是二级索引,又叫辅助索引(secondary indexes)
-
确保主键无意义性
主键不应该与业务相关(其实个人感觉业务相关的字段也很难满足上述两个原则)
在前面的基础上,我们来看下面几种策略:
- UUID: 缺点:非递增,过长(截取后又无法保证唯一性); 优点:在分布式环境下能保证唯一性
- 自增主键: 缺点:1.分布式下数据库一般会分库,主键可能会重复 2.把一个自增列的表复制到另外的表自增列会重新开始 3.主键是可预测的(没有遇到过具体的问题,不清楚); 优点:递增且长度可控,实现简单。
- SnowFlake:推特提出的一种分布式自增ID算法。https://segmentfault.com/a/1190000011282426
考虑到当前项目没有分布式的需求,所以采用了相对简单的自增主键策略。
-
外键约束
外键是不可能加的,这辈子都不可能加的,影响性能不说,对个人开发的项目而言外键反而会带来麻烦。(希望能有高手指点一下外键的取舍)
索引策略
- 对于性别,状态等取值情况比较少,重复值比较多的字段,不需要加索引。索引是需要IO开销的,假设你从100w条数据中取出50w条数据,那你相对需要访问50万次索引,再访问50万次表,优化器认为此索引使用的成本过高,不会使用索引。
- 复合索引:复合索引有一个规则,从左到右使用索引中的字段,如(a,b,c)三个字段建立索引,(b,c)作为条件查询是不走索引的。
- 尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),比如我们不应给性别这种字段加索引。
- 更多索引优化可以参考美团开源的SQLAdvisor。
B+Tree详解
可以参考美团的技术博客,这里面讲的非常透彻。
网友评论