普通索引和唯一索引

这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

在不同的业务场景下,普通索引和唯一索引有什么异同呢?

通过一个实例来看一下两者具体有什么区别

EG:按身份证号查询用户姓名 select name from CUser where id_card = 'XXXXXX';

  • 给id_card字段创建唯一索引
  • 创建一个普通索引

查询语句的性能影响

假设执行的查询语句是 select id from T where k = 5 先通过B+树从树根开始 - 按层搜索到叶子节点 - 数据页内部通过二分法来定位记录

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

性能差异 - 微乎其微

InnoDB - 数据按数据页为单位来读写 当需要读一条记录 - 以页为单位,将整体读入内存 - InnoDB中,每个数据页的大小默认是16KB. 因此,引擎按页读写 - 当找到k = 5的记录时,所在的数据页就都在内存里了

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

更新过程的性能影响

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

change buffer

可以持久化的数据 - 在内存中有拷贝,也会被写入到磁盘中 操作应用到原数据页 - 得到最新结果的过程称为merge - 访问这个数据页会触发merge - 系统又后台线程会定期merge - 数据库正常关闭(shutdown)的过程中 - merge 将更新操作先记录在change buffer - 减少读磁盘 - 语句的执行速度会得到明显提升 数据读入内存 - 需要占用 buffer pool - 该方式能够避免占用内存,提高内存利用率

change buffer 中用的是buffer pool里的内存,因此不能无线增大 change buffer的大小 - 可通过参数innodb_change_buffer_max_size来动态设置

- 参数 为 50 - change buffer的大小最多只能占用buffer pool的50%
复制代码

什么条件下可以使用change buffer?

唯一索引 - 所有的更新操作 - 都要先判断这个操作是否违反唯一性约束 - 必须要将数据页读入内存中才能判断 - 没必要使用 change buffer **唯一索引的更新不能使用change buffer - 普通索引可以使用 **

在这张表中插入一个新纪录(4, 400)的话,InnoDB的处理流程是怎样的?

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

    1. 唯一索引 - 找到3和5之间的位置 - 判断到没有冲突 - 插入这个值 - 语句执行结束
    2. 普通索引 - 找到3和5之间的位置 - 插入这个值 - 语句执行结束

    普通索引和唯一索引对更新语句性能影响的差别,只是一个判断 - 耗费微小的CPU时间

  2. 该记录要更新的目标页不在内存中

    1. 唯一索引 - 将数据页读入内存中 - 判断到没有冲突 - 插入这个值 - 语句执行结束
    2. 普通索引 - 更新记录在change buffer,语句执行结束

    将数据从磁盘读入内存涉及随机IO的访问,是数据库里面成本最高的操作之一 - change buffer因为减少了随机磁盘的访问 - 对更新性能的提升很明显!

change buffer的使用场景

merge时是真正进行数据更新的时刻 - change buffer的主要目的就是将记录的变更动作缓存下来 - 一个数据页做merge之前 - change buffer记录的变更越多 (这个页面上要更新的次数越多) - 收益越大

写多读少的业务 - 页面在写完后马上被访问到的概率较小 - change buffer的使用效果最好 - 账单类、日志类系统

写入后马上会做查询 - 将更新先记录在change buffer - 但之后由于马上要访问这个数据页 - 会立即触发merge过程 - 这样随机访问IO的次数不会减少 - 增加了change buffer的维护代价 - change buffer反而起到了副作用

索引选择和实践

查询能力 - 没差别

更新性能 - 尽量选择普通索引
普通索引 + change buffer - 数据量大的表的更新优化

change buffer和redo log

EG: insert into t(id, k) values(id1, k1), (id2, k2)
假设当前K索引树的状态,查找到位置后,k1所在的数据页在内存(InnoDB buffer pool)中, k2所在的数据页不在内存中
1. Page1在内存中,直接更新内存
2. Page2不在内存中 - 内存的change buffer区域记录下“要往page2插入一行"的信息
3. 将上述两个动作记入redo log中
事务就可以完成了 - 执行这条更新语句成本很低 - 写了两处内存 - 写了一处磁盘【两次操作合在一起写了一次磁盘】
这之后的读请求
1. 读语句在更新语句后不久 - 内存中的数据还在 - 1. 读Page1 - 直接从内存中返回 - 不需从redo log里把数据更新以后才可以返回
1. 读Page2 - 把Page2从磁盘读入内存 - 应用change buffer里的操作日志 - 生成一个正确的版本并返回结果
redo log 节省随机写磁盘的IO消耗【转成顺序写】 change buffer节省随机读磁盘的IO消耗

image.png

image.png

猜你喜欢

转载自juejin.im/post/7034117681145970724
今日推荐