普通索引和唯一索引

322 阅读4分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

从性能方面开始考虑 【业务代码已经保证不会写入重复数据的情况下

查询过程

1. 普通索引 - 查找到满足条件的第一个记录后 - 需要查找下一个记录 - 直到碰到第一个不满足条件的记录  
1. 唯一索引 - 索引定义的唯一性 - 查找到第一个满足条件的记录 - 停止继续检索   

InnoDB的数据是按数据页为单位来读写的 - 需要一条记录时,不是将这个记录本身从磁盘读出来,而是以页为单位,将其整体读入内存

普通索引 - 多做的那次“查找和判断下一条记录”的操作 - 只需要一次指针寻找和一次计算

更新过程

change buffer - 持久化数据【在内存中有拷贝,也会被写入磁盘】

需要更新一个数据页时 - 数据页在内存中 - 直接更新
- 数据页不在内存中 - 不影响数据一致性前提下 - 更新操作缓存在change buffer - 下次查询需要访问这个数据页时 - 数据页读入内存 - 执行change buffer 中与这个页有关的操作 - 保证数据逻辑的正确性

用的是buffer pool中的内存 - 不能无限大 - innodb_change_buffer_max_size 来动态设置 EG:50 - change buffer的大小最多只能占用buffer pool的50%

merge
1. change buffer中的操作应用到原数据页  【访问数据页时触发】
1. 系统后台线程定期merge 
1. 数据库正常关闭【shutdown】  
优点
1. 减少读磁盘 - 语句执行速度明显提升 
1. 数据读入内存 - 占用buffer pool - ∴ 避免占用内存,提高内存利用率  

什么条件可以使用change buffer?

  1. 唯一索引 - 先判断该操作是否违反唯一性约束 必须将数据页读入内存才能判断 - 无需使用change buffer ∴ 普通索引中使用change buffer

插入新记录(4, 400)的处理流程

  1. 该记录要更新的目标页在内存中 -

    1. 唯一索引 - 找到3和5的位置 - 判断到没有冲突 - 插入该值 - 语句执行结束
    2. 普通索引 - 找到3和5的位置 - 插入该值 - 语句执行结束
      普通索引和唯一索引对更新语句性能影响的差别只是一个判断 - 耗费微小的CPU时间
  2. 该记录要更新的目标页不在内存中 -

    1. 唯一索引 - 将该记录所在数据页读入到内存 - 找到3和5的位置 【重复在内存中的操作】
    2. 普通索引 - 将更新记录在change buffer中,语句执行结束
      数据从磁盘中读入内存涉及随机IO的访问 - 数据库成本最高的操作之一 change buffer减少了磁盘访问 - 更新性能的提升会很明显

change buffer使用场景

写多读少 - change buffer中记录的变更越多 - 收益越大 - 账单类、日志类系统 普通索引 + change buffer【尽量开大】=> 数据量大的表的更新优化 尽量选择普通索引, 如果所有更新后面马上伴随着有该记录的查询 - 关闭change buffer【反而增加了change buffer的维护代价】

首先要保证业务正确,业务正确性优先 - 如果碰上大量插入数据慢,内存命中率低的时候 - 多一条排查思路 + 一些“归档库”的场景【历史数据要保存在归档库中 - 此时归档数据已经确保没有唯一键冲突 - 普通索引提高归档效率】

change buffer和redo log

WAL提升性能的核心机制 - 尽量减少随机读写

写入

insert into t(id, k) values(id1, k1), (id2, k2); - k1所在数据页在内存中,k2所在数据页不在内存中

image.png

内存、redo log、数据表空间(t.ibd)、系统表空间(ibdata1)

  1. page1在内存中 - 直接更新内存
  2. page2不在内存 - 从内存的change buffer区域记录该条操作
  3. 将上述两个动作记入redo log中
    执行该更新语句成本很低 - 写了两处内存,写了一处磁盘【两个操作合在一起写了一次磁盘】【顺序写】

读请求

select * from t where k in(k1, k2)
若语句发生在更新语句后不久 - 内存中的数据还在 - 此时两个读操作与系统表空间和redo log无关

image.png

  1. 读Page1时,直接从内存中返回 WAL技术后,如果读数据,是不是一定要读盘?一定要从redo log中把数据更新以后才可以返回? 不用 - 如上图状态 - 磁盘中还是之前的数据 - 但是直接从内存返回正确结果【此时内存中是最新值、redo log中记录的是最新操作,磁盘中还未刷新成最新值
  2. 读Page2时,把Page2读入内存,应用change buffer里面的操作日志,生成一个正确的版本并返回结果 - 需要读page2时,该数据页才会被读入内存

redo log节省随机写磁盘的IO消耗【转成顺序写】 change buffer节省随机读磁盘的IO消耗