ChangeBuffer是InnoDB缓存区的一种特殊的数据结构,当用户执行SQL对非唯一索引进行更改时,如果索引对应的数据页不在缓存中时,InnoDB不会直接加载磁盘数据到缓存数据页中,而是缓存对这些更改操作。这些更改操作可能由插入、更新或删除操作(DML)触发。缓存区的更改操作会在磁盘数据被其它读操作加载到缓存中时合并到对应的缓存数据页中。

ChangeBuffer

InnoDB ChangeBuffer的官方示意图如下所示,从图中可以看出以下信息:

  1. ChangeBuffer用于存储SQL变更操作,比如Insert/Update/Delete等SQL语句;
  2. ChangeBuffer中的每个变更操作都有其对应的数据页,并且该数据页未加载到缓存中;
  3. 当ChangeBufferd中变更操作对应的数据页加载到缓存中后,InnoDB会把变更操作Merge到数据页上;
  4. InnoDB会定期加载ChangeBuffer中操作对应的数据页到缓存中,并Merge变更操作;

基于个人理解并参考官方的ChangeBuffer示例图,我绘制了以下更为直观的的ChangeBuffer示例图:

ChangeBuffer的作用

我们知道InnoDB推荐使用自增主键,插入时主键值时递增的,可以顺序访问。与聚簇索引不同,二级索引通常是不是唯一的,并且以相对随机的顺序插入。类似的,二级索引的更新和删除经常也会影响索引树中不相邻的二级索引数据页。

对于二级索引数据变更引起的随机访问,如果每次都进行磁盘IO显然会影响数据库的性能。因此InnoDB不会立即执行数据页不在缓存中的二级索引的变更操作,而是先将变更操作缓存起来,在某个时刻再将某一个数据页上面的所有变更操作合并到该数据页上,通过变更操作缓存(ChangeBuffer)可合并同一个数据页上的大量随机访问I/O。

ChangeBuffer工作流程

变更操作什么时候放入ChangeBuffer

并不是数据库中的所有操作都会进入ChangeBuffer,满足以下条件的数据库语句,在执行阶段不会修改数据页,而是会进入ChangeBuffer,

  1. SQL会修改数据库中的数据;
  2. SQL语句不涉及唯一键的校验;
  3. SQL语句不需要返回变更后的数据;
  4. 涉及的数据页不在缓存中;

ChangeBuffer合并到原数据页

我们知道,ChangeBuffer中缓存了变更操作,这些操作最终需要合并到数据库的数据页,合并过程称为Merge,那么在什么场景下会触发ChangeBuffer的Merge操作呢?

  1. 访问变更操作对应的数据页;
  2. InnoDB后台定期Merge;
  3. 数据库BufferPool空间不足;
  4. 数据库正常关闭时;
  5. RedoLog写满时;

为什么ChangeBuffer只缓存非唯一索引数据

ChangeBuffer仅仅适用于变更的数据未为非唯一索引的情况,如果变更操作修改的数据为唯一索引或者主键数据,那么InnoDB无法把变更操作缓存到ChangeBuffer,这是为什么呢?

以一张用户表为例,用户表包含主键ID、年龄、姓名和性别四个字段,其中年龄添加了非唯一索引,初始数据及建表语句如下所示:

用户ID 1 2 3 4 5 6 7 8 9
姓名 陈尔 张散 李思 王舞 赵流 孙期 周跋 吴酒 郑史
性别
年龄 5 10 20 28 35 56 25 80 90
create table user_info
(
id int primary key,
age int not null,
name varchar(16),
sex bool,
key(age)
)engine=InnoDB;

非唯一索引更新

假设我们使用SQL语句update user_info set age=6 where id=1修改ID=1的用户的年龄为6,该操作会同时修改年龄索引以及行数据中的年龄,更新步骤如下:

  • 如果需要更改的年龄索引页和行数据页在缓存中,直接更新缓存中的数据,并把数据页标记为脏页;
  • 如果需要更改的年龄索引页和行数据页不在缓存中,直接把SQL语句update user_info set age=6 where id=1存储到ChangeBuffer;

唯一索引更新

假设我们使用SQL语句update user_info set id=2 where id=1修改ID=1的用户的ID为2,该操作会同时修改聚簇索引和行数据,更新步骤如下:

  • 如果需要更改的聚簇索引和行数据页在缓存中,直接更新缓存中的数据,并把数据页标记为脏页;
  • 如果需要更改的聚簇索引页和行数据页不在缓存中,需要把对应的数据页加载到缓存中,判断修改之后ID是不是符合唯一键约束,然后修改缓存中的数据;

可以看到,由于唯一索引需要进行唯一性校验,所以对唯一索引进行更新时必须将对应的数据页加载到缓存中进行校验,从而导致ChangeBuffer失效。

普通索引还是唯一索引

通过以上分析,我们知道唯一索引无法使用ChangeBuffer,那么我们实际使用过程中应该使用普通索引还是唯一索引呢?

从等值查询性能角度来看

  • 普通索引在查找到第一个满足条件的数据之后,需要继续向后查找满足条件的数据;
  • 唯一索引在查找到第一个满足条件的数据之后,不需要再次向后查找,因为索引具有唯一性;

二者之间只相差一条记录,这个一条记录会带来多大的性能差距呢?答案是,微乎其微。因为InnoDB引擎是以页为单位读取数据的,读取一条数据时,往往会将临近的数据也读到内存,所以多向后查询几条数据带来的性能差别微乎其微。

从索引修改角度来看

由于非唯一索引无法使用ChangeBuffer,对索引的修改会引起大量的磁盘IO,影响数据库性能。

综上可知,如果不是业务中要求数据库对某个字段做唯一性检查,我们最好使用普通索引而不是唯一索引。

ChangeBuffer适用场景

什么情况下ChangeBuffer会有较大的性能提升呢?

  1. 数据库大部分索引是非唯一索引;
  2. 业务是写多读少,或者不是写后立刻读取;

不适合使用ChangeBuffer的场景与之对应:

先说什么时候不适合,如上文分析,当:

  1. 数据库都是唯一索引;
  2. 写入数据后,会立刻读取;

ChangeBuffer相关参数

  • innodb_change_buffer_max_size: 配置写缓冲的大小,占整个缓冲池的比例,默认值是25%,最大值是50%。

    写多读少的业务,才需要调大这个值。

  • innodb_change_buffering: 配置哪些写操作启用写缓冲,可以设置成all/none/inserts/deletes等。

我是御狐神,欢迎大家关注我的微信公众号:wzm2zsd

本文最先发布至微信公众号,版权所有,禁止转载!

InnoDB学习(二)之ChangeBuffer的更多相关文章

  1. emberjs学习二(ember-data和localstorage_adapter)

    emberjs学习二(ember-data和localstorage_adapter) 准备工作 首先我们加入ember-data和ember-localstorage-adapter两个依赖项,使用 ...

  2. ReactJS入门学习二

    ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事件 如何在JSX中如何使用 ...

  3. TweenMax动画库学习(二)

    目录            TweenMax动画库学习(一)            TweenMax动画库学习(二)            TweenMax动画库学习(三)            Tw ...

  4. Hbase深入学习(二) 安装hbase

    Hbase深入学习(二) 安装hbase This guidedescribes setup of a standalone hbase instance that uses the local fi ...

  5. Struts2框架学习(二) Action

    Struts2框架学习(二) Action Struts2框架中的Action类是一个单独的javabean对象.不像Struts1中还要去继承HttpServlet,耦合度减小了. 1,流程 拦截器 ...

  6. Python学习二:词典基础详解

    作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/7862377.html 邮箱:moyi@moyib ...

  7. Quartz学习--二 Hello Quartz! 和源码分析

    Quartz学习--二  Hello Quartz! 和源码分析 三.  Hello Quartz! 我会跟着 第一章 6.2 的图来 进行同步代码编写 简单入门示例: 创建一个新的java普通工程 ...

  8. SpringCloud学习(二):微服务入门实战项目搭建

    一.开始使用Spring Cloud实战微服务 1.SpringCloud是什么? 云计算的解决方案?不是 SpringCloud是一个在SpringBoot的基础上构建的一个快速构建分布式系统的工具 ...

  9. DjangoRestFramework学习二之序列化组件、视图组件 serializer modelserializer

      DjangoRestFramework学习二之序列化组件.视图组件   本节目录 一 序列化组件 二 视图组件 三 xxx 四 xxx 五 xxx 六 xxx 七 xxx 八 xxx 一 序列化组 ...

  10. SpringMVC入门学习(二)

    SpringMVC入门学习(二) ssm框架 springMVC  在上一篇博客中,我简单介绍了一下SpringMVC的环境配置,和简单的使用,今天我们将进一步的学习下Springmvc的操作. mo ...

随机推荐

  1. MVC +Jqyery+Ajax 实现弹出层提醒

    CSS部分: /*登录提示*/ * {margin: 0; padding: 0; } .layer { width: 350px; padding: 20px; background: #fff; ...

  2. Kill杀死Linux中的defunct进程(僵尸进程)

    一.什么是defunct进程(僵尸进程)? 在 Linux 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程.当用ps命令观察进程的执行状 ...

  3. F. Mattress Run 题解

    F. Mattress Run 挺好的一道题,对于DP的本质的理解有很大的帮助. 首先要想到的就是将这个拆成两个题,一个dp光求获得足够的夜晚的最小代价,一个dp光求获得足够的停留的最小代价. 显然由 ...

  4. centos7 使用iptables

    关闭selinux,不关闭时,iptables不读取配置文件 重启生效 centos7 中默认的防火墙是firewalld,使用iptables需要先关闭firewalld防火墙,安装iptables ...

  5. shell脚本 PHP+swoole的安装

    #!bin/bash set -e # Check if user is root if [ $(id -u) != "0" ]; then echo "Error: p ...

  6. 【java+selenium3】自动化截图 (十四)

    一.截图 1. Firefox浏览器截图 FirefoxDriver firefoxDriver = new FirefoxDriver(); firefoxDriver.getScreenshotA ...

  7. Go语言核心36讲(Go语言进阶技术十五)--学习笔记

    21 | panic函数.recover函数以及defer语句 (上) 在本篇,我要给你展示 Go 语言的另外一种错误处理方式.不过,严格来说,它处理的不是错误,而是异常,并且是一种在我们意料之外的程 ...

  8. vue强制组件重新渲染

    有时候,依赖 Vue 响应方式来更新数据是不够的,相反,我们需要手动重新渲染组件来更新数据.或者,我们可能只想抛开当前的DOM,重新开始.那么,如何让Vue以正确的方式重新呈现组件呢? 强制 Vue ...

  9. 远程连接linux | Xshell和Xftp下载安装

    为什么需要远程登录linux 公司开发时候, 具体的情况是这样的: Linux 一般作为服务器使用,而服务器一般放在机房,你不可能在机房操作你的 Linux 服务器.这时我们就需要远程登录到Linux ...

  10. python实现其它形态学操作

    目录: (一) 顶帽(原图像与开操作图像的差值)(二) 黑帽(原图像与闭操作图像的差值)(三) 形态学梯度  (1)基本梯度(膨胀后的图像与腐蚀后的图像差值)  (2)内部梯度(原图像减去腐蚀后的图像 ...