你分得清MySQL普通索引和唯一索引了吗?

0 概念区分

  • 普通索引和唯一索引普通索引可以重复 , 唯一索引和主键一样不能重复 。 唯一索引可以作为数据的一个合法验证手段 , 例如学生表的身份证号码字段 , 我们认为规定该字段不得重复 , 那么就使用唯一索引 。 (一般设置学号字段为主键)
  • 主键和唯一索引主键保证数据库里面的每一行都是唯一的 , 比如身份证 , 学号等 , 在表中要求唯一 , 不重复 。 唯一索引的作用跟主键的作用一样 。 不同的是 , 在一张表里面只能有一个主键 , 主键不能为空 , 唯一索引可以有多个 , 唯一索引可以有一条记录为空 , 即保证跟别人不一样就行 。 比如学生表 , 在学校里面一般用学号做主键 , 身份证则弄成唯一索引;而到了教育局 , 他们就把身份证号弄成主键 , 学号换成了唯一索引 。 选谁做表的主键 , 要看实际应用 , 主键不能为空 。
1 示例一个市民系统 , 每个人都有个唯一身份证号;业务代码已保证不会写入两个重复的身份证号;如果市民系统需要按照身份证号查姓名 , 就会执行类似SQL:
select name from CUser where id_card = 'xxxxxxxyyyyyyzzzzz';相信你一定会在id_card字段上建索引 。
由于身份证号字段比较大 , 不建推荐把身份证号做主键 。 因此现在有两个选择
  1. 给id_card字段创建唯一索引
  2. 创建一个普通索引
如果业务代码已保证不会写入重复的身份证号 , 那这两个选择逻辑上都正确 。
但从性能角度考虑 , 唯一索引还是普通索引呢?假设字段 k 上的值都不重复 。
  • InnoDB的索引组织结构接下来从这两种索引对查询语句和更新语句的性能影响来进行分析 。
2 查询过程查询语句
select id from T where k=5该语句在索引树查找的过程:先通过B+树从树根开始 , 按层搜索到叶节点 , 即图中右下角的数据页 , 然后可认为数据页内部是通过二分法定位记录 。
  • 对普通索引 , 查找到满足条件的第一个记录(5,500)后 , 需查找下个记录 , 直到碰到第一个不满足k=5条件的记录
  • 对唯一索引 , 由于索引定义了唯一性 , 查找到第一个满足条件的记录后 , 就会停止检索 。
该不同点带来的性能差距会有多少呢?微乎其微!
InnoDB数据是按数据页为单位读写 。 即当需读一条记录时 , 并非将该记录本身从磁盘读出 , 而是以页为单位 , 将其整体读入内存 。
InnoDB中 , 每个数据页的大小默认是16KB 。
【你分得清MySQL普通索引和唯一索引了吗?】因引擎按页读写 , 所以 , 当找到k=5记录时 , 它所在数据页就都在内存了 。 对普通索引 , 要多做的那一次“查找和判断下一条记录”的操作 , 就只需要一次指针寻找和一次计算 。 如果k=5记录刚好是该数据页的最后一个记录 , 那么要取下个记录 , 必须读取下个数据页 , 操作会稍微复杂 。 对于整型字段 , 一个数据页可存近千个key , 因此这种情况概率很低 。 所以 , 计算平均性能差异时 , 仍可认为该操作成本对现在的CPU可忽略不计 。
3 更新过程3.1 change buffer需更新一个数据页时
  • 若数据页在内存 , 直接更新
  • 若该数据页不在内存 , 在不影响数据一致性前提下 , InooDB会将这些更新操作缓存在change buffer , 无需从磁盘读入该数据页 。 在下次查询需要访问该数据页时 , 将数据页读入内存 , 然后执行change buffer中与这个页有关的操作 。 通过该方式就能保证这个数据逻辑的正确性 。
虽然叫change buffer , 实际上是可持久化的数据 。 即change buffer在内存中有拷贝 , 也会被写进磁盘 。
3.2 merge3.2.1 概念将change buffer中的操作应用到原数据页 , 得到最新结果的过程 。
3.2.2 触发时机访问该数据页会触发merge系统有后台线程会定期merge在数据库正常关闭(shutdown)的过程中 , 也会执行merge 。
若能将更新操作先记录在change buffer , 减少读盘 , 语句执行速度会明显提升 。 且数据读入内存需要占用buffer pool , 所以该方式还能避免占用内存 , 提高内存利用率 。
3.3 何时用change buffer对于唯一索引 , 所有更新操作要先判断该操作是否违反唯一性约束 。
比如 , 要插入(4,400)记录 , 要先判断表中是否已存k=4记录 , 而这必须要将数据页读入内存才能判断 。 如果都已经读入到内存 , 那直接更新内存会更快 , 就没必要使用change buffer 。 因此 , 唯一索引的更新就不能使用change buffer , 实际上也只有普通索引可使用 。