Mysql:小主键,大问题

今日格言:让一切回归原点,回归最初的为何。mysql

本篇讲解 Mysql 的主键问题,从为何的角度来了解 Mysql 主键相关的知识,并拓展到主键的生成方案问题。不再怕被问到 Mysql 时只知道 CRUD 了。算法

1、为何须要主键

  1. 数据记录需具备惟一性(第一范式)
  2. 数据须要关联 join
  3. 数据库底层索引用于检索数据所需

如下废话连篇,能够直接跳过到下一节。sql

信息是用来消除随机不定性的东西”(香农)。人经过得到、识别天然界和社会的不一样信息来区别不一样事物,得以认识和改造世界。数据是反映客观事物属性的记录,是信息的具体表现形式。数据通过加工处理以后,就成为信息;而信息须要通过数字化转变成数据才能存储和传输。数据库就是用于存储数据记录的。既已如此,记录即是具备肯定性(相对)的信息,其肯定性即惟一性。咱们得出第一条缘由:mongodb

1.数据记录需具备惟一性数据库

世界是由客观存在及其关系组成的。数据是数字化和模型化的存在关系。数据除了自己的描述价值外,其价值还在于其相互关联性。为实现关联的准确性,数据须要有对外相互关联的标识。因此体如今数据存储上,主键的第二做用,也是存在的第二因素即:c#

2.数据须要关联数据结构

数据用于描述客观实在的,自己没有意义。只有在根据主观需求组织以后,经过必定方式知足人认识事物的过程才具备了意义。因此数据须要被检索,被组织。则主键第三个做用:并发

3.数据库底层索引用于检索数据所需分布式

2、为何主键不宜过长

这个问题的点在上。那有什么优点?(嘿嘿嘿,内涵)—— 短不占空间。但这么点磁盘空间相对整个数据量来讲微不足道,并且咱们通常不怎么用到主键列。那么缘由应该在上,并且和原始数据关系不大。以此天然得出和索引相关,并且和索引读取相关。那么为何长主键在索引中会影响性能?性能

上面是 Innodb 的索引数据结构。左边是聚簇索引,经过主键定位数据记录。右边是二级索引,对列数据作索引,经过列数据查找数据主键。若是经过二级索引查询数据,流程如图上所示,先从二级索引树上搜索到主键,而后在聚簇索引上经过主键搜索到数据行。其中二级索引的叶子节点是直接存储的主键值,而不是主键指针。因此若是主键太长,一个二级索引树所能存储的索引记录就会变少,这样在有限的索引缓冲中,须要读取磁盘的次数就会变多,因此性能就会降低。

3、为何建议使用自增 ID

InnoDB 使用聚簇索引,如上图所示,数据记录自己被存于主索引(一颗 B+Tree)的叶子节点上。这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放,所以每当有一条新的记录插入时,MySQL 会根据其主键将其插入适当的节点和位置,若是页面达到装载因子(InnoDB 默认为 15/16),则开辟一个新的页(节点)。

若是表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。这样就会造成一个紧凑的索引结构,近似顺序填满。因为每次插入时也不须要移动已有数据,所以效率很高,也不会增长不少开销在维护索引上,以下图左侧所示。不然因为每次插入主键的值近似于随机,所以每次新记录都要被插到现有索引页的中间某个位置,MySQL 不得不为了将新记录插到合适位置而移动数据,以下图右侧所示,这样就形成了必定的开销。因为此,Mysql 为维护索引可能须要频繁的刷新缓冲,增长了方法磁盘 IO 的次数,并且时常须要对索引结构进行重组织。

4、业务 Key VS 逻辑 Key

业务 Key,即便用具备业务意义的 id 做为 Key,好比使用订单流水号做为订单表的主键 Key。逻辑 Key,即无关业务的 Key,按某种规则生成 Key,如自增 Key。

业务 Key 的优势

  • Key 具备业务意义,在查询时能够直接做为搜索关键字使用
  • 不须要额外的列和索引空间
  • 能够减小一些 join 操做。

业务 Key 的缺点

  • 当业务发生变化时,有时须要变动主键
  • 涉及多列 Key 时比较难操做
  • 业务 Key 每每比较长,所占空间更大,致使更大的磁盘 IO
  • 在 Key 肯定前不能持久化数据,有时咱们没有在肯定数据 Key 时,就想先添加一条记录,以后再更新业务 Key
  • 设计一个兼具易用和性能的 Key 生成方案比较难

逻辑 Key 的优势

  • 不会由于业务的变更而须要修改 Key 逻辑
  • 操做简单,且易于管理
  • 逻辑 Key 每每更小,性能更优
  • 逻辑 Key 更容易保证惟一性
  • 更易于优化

逻辑 Key 缺点

  • 查询主键列和主键索引须要额外的磁盘空间
  • 在插入数据和更新数据时须要额外的 IO
  • 更多的 join 可能
  • 若是没有惟一性策略限制,容易出现重复的 Key
  • 测试环境和正式环境 Key 不一致,不利于排查问题
  • Key 的值没有和数据关联,不符合三范式
  • 不能用于搜索关键字
  • 依赖不一样数据库系统的具体实现,不利于底层数据库的替换

5、主键生成

通常状况下,咱们都使用 Mysql 的自增 ID,来做为表的主键,这样简单,并且从上面讲到的来看,性能也是最好的。可是在分库分表的状况状况下,自增 ID 则不能知足需求。咱们能够来看看不一样数据库生成 ID 的方式,也看一些分布式 ID 生成方案。利于咱们思考甚至实现本身的分布式 ID 生成服务。

数据库的实现

Mysql 自增

Mysql 在内存中维护一个自增计数器,每次访问 auto-increment 计数器的时候, InnoDB 都会加上一个名为AUTO-INC 锁直到该语句结束(注意锁只持有到语句结束,不是事务结束)。AUTO-INC 锁是一个特殊的表级别的锁,用来提高包含 auto_increment 列的并发插入性。

在分布式的状况下,其实能够独立一个服务和数据库来作 id 生成,依旧依赖 Mysql 的表 id 自增能力来为第三方服务统一辈子成 id。为性能考虑能够不一样业务使用不一样的表。

Mongodb ObjectId

Mongodb 为防止主键冲突,设计了一个 ObjectId 做为主键 id。它由一个 12 字节的十六进制数字组成,其中包含如下几部分:

  1. Time:时间戳。4 字节。秒级。

  2. Machine:机器标识。3 字节。通常是机器主机名的散列值,这样就确保了不一样主机生成不一样的机器 hash 值,确保在分布式中不形成冲突,同一台机器的值相同。

  3. PID:进程 ID。2 字节。上面的 Machine 是为了确保在不一样机器产生的 objectId 不冲突,而 pid 就是为了在同一台机器不一样的 mongodb 进程产生的 objectId 不冲突。

  4. INC:自增计数器。3 字节。前面的九个字节保证了一秒内不一样机器不一样进程生成的 objectId 不冲突,自增计数器,用来确保在同一秒内产生的 objectId 也不会发现冲突,容许 256 的 3 次方等于 16777216 条记录的惟一性。

Cassandra TimeUUID

Cassandra 使用下面规则生成一个惟一的 id:time + MAC + sequence

方案

  1. Zookeeper 自增:经过 zk 的自增机制实现。
  2. Redis 自增:经过 Redis 的自增机制实现。
  3. UUID:使用 UUID 字符串做为 Key。
  4. snowflake 算法:和 Mongodb 的实现相似,1位符号位 + 41位时间戳(毫秒级)+ 10位数据机器位 + 12位毫秒内的序列

开源实现

  1. 百度 UidGenerator:基于snowflake算法。
  2. 美团 Leaf:同时实现了基于 Mysql 自增(优化)和 snowflake 算法的机制。

推荐系列

列式存储
时间序列数据库(TSDB)初识与选择
十分钟了解 Apache Druid
Apache Druid 底层存储设计
Apache Druid 的集群设计与工做流程
Mysql 大表问题和解决

想了解更多数据存储相关知识,请关注个人公众号。

MageByte

相关文章
相关标签/搜索