mysql 索引结构
Innodb 架构
Buffer Pool
用于加速读Change Buffer
用于没有非唯一索引的加速写
Log Buffer
用于加速redo log写
自适应Hash索引
主要用于加快查询页。在查询时,Innodb通过监视索引搜索的机制来判断当前查询是否能走Hash索引。比如LIKE运算符和% 通配符就不能走。
缓冲池 buffer pool
- 数据是存在磁盘的idb文件中,交互时速度必然是比较慢的。这时就需要将数据加载到内存中。
- innodb逻辑上的最小单位是页(16k),那么每次加载的页到内存的区域(预读),就是buffer pool
- 下一次取数据(数据页或者索引页)时,会优先判断在不在buffer页中。修改时,会修改内存中的数据,当buffer pool中数据和磁盘不一致时,这时就叫脏页。会有工作线程会定时同步,这个操作就叫刷脏
- mysql改进的LRU:
- buffer pool 分为了young(5/8)和old(5/3)两个部分
- 数据会在用户的sql用到了页中的数据,或者mysql猜测你很可能会用到的数据-预读,这两种情况加载到buffer pool
- 数据优先进入old,old满了会移出队列尾部。young区呢?
用户sql使用的页,会被移入young
(且需要在old中待满配置的秒数)。而预读的数据,没被使用则会一直在old区,直到被清除
问题: 当一次大表扫描,会导致大量数进入young?
解决:mysql防止一次扫描数据过大,替换了大量热数据,有参数控制 innodb_old_blocks_time(需要在old区待满多少秒,且再被用户sql调用时,才会进入young)。
change Buffer
当数据不存在于buffer pool时,且更新操作的数据没有唯一索引
(非唯一,非主键,不是降序索引),不存在重复数据的情况
,可以直接在change buffer中记录操作。原因: 这样的数据,不需要校验唯一性,直接更新即可,没必要加载数据到buffer pool,而且查询时,数据也是随机分布在不同的页上面,可能需要加载多个页的数据,产生大量IO操作。
change buffer的数据什么时候会同步出去:
访问这个数据页时、执行merge操作
,将修改操作合并,后面同步到磁盘空闲时后台线程处理
shoutdown
Log Buffer
官方描述:日志缓冲区是保存要写入磁盘上日志文件的数据的内存区域
。日志缓冲区大小由 innodb_log_buffer_size变量定义。默认大小为 16MB。日志缓冲区的内容会定期刷新到磁盘。大的日志缓冲区使大事务能够运行,而无需在事务提交之前将redo log 数据写入磁盘。因此,如果您有更新、插入或删除许多行的事务,则增加日志缓冲区的大小可以节省磁盘 I/O。
用来记录操作日志,先写入到log buffer ,然后事务提交,或者缓冲区满了的时候写入磁盘文件 redo log
redo log
-
是一种基于磁盘的数据结构,用于
在崩溃恢复期间纠正由不完整事务写入的数据
。 -
作用: 主要用来做崩溃恢复,用来实现事务持久性
-
内部机制:
数据存在缓冲区,没有进行刷盘操作时,如果数据库宕机或者重启,会导致数据丢失
。 redo log的作用就是,数据写入缓存后,写一下redo log ,记录的不是最终的结果,而是要进行的操作
。崩溃恢复时,将redo log数据重新加载到缓冲区。 -
策略: 0(延时写) 1 2(实时写,延时刷) 默认是1 事务操作实时记录并刷新数据页
疑问:写到日志文件还是一次磁盘io,为什么不直接写到数据页 ?
- 顺序IO和随机IO 写日志是顺序io,效率高(可以参考Kakfa)
- 记录的是操作,不是结果,数据量其实不大
Adaptive Hash Index
osCache
磁盘中主要组成部分是5类表空间
-
**系统表空间(ibdata1):**组成:
数据字典(表和索引的元信息) 、双写缓冲区 、undo log、Change buffer
- 双写缓冲: Innodb从buffer pool flushed数据页过程中,如果出现崩溃等异常情况(简单理解就是,刷数据时出现刷一半崩溃的情况,内存被清空了,磁盘也刷了一半数据,这种场景需要处理), innodb 为我们提供双写机制,先拷贝一份数据的副本,如果崩溃,就将副本恢复。虽然数据需要双写,但是并不是两倍的IO开销,因为写入缓冲区是一次 large sequential chunk (官网这一句理解不了,暂时理解为顺序写入开销不大)
-
独占表空间: 可以为每个表设置为独占表空间
-
临时表空间:
-
通用表空间:
redo log:记录数据到磁盘,用于崩溃恢复,保证事务的持久性
undo log:撤销回滚日志 记录事务发生之前的状态,如果发生异常,可以使用undo log回滚,存储的是旧数据,并发事务时使用回滚段来控制。
bin log ,server层的日志。主要是用来主从复制(原理就是读取主库的bin log,执行一遍sql),数据恢复。记录所有的sql语句,有点aof的味道,可以将操作重复实现数据恢复。和redo log不一样,他的内容可以追加,没有大小限制
Mysql 内部模块
update sql 流程
- 查询到我们要修改的那条数据,我们这里称做 origin,返给执行器
- 在执行器中修改数据,称为 modification
- 将modification刷入内存,Buffer Pool的 Change Buffer
- 引擎层:
记录undo log (实现事务原子性)
- 引擎层:
记录redo log (崩溃恢复使用)
- 服务层:
记录bin log(记录DDL)
- 返回更新成功结果
- 数据等待被工作线程刷入磁盘
主从复制方案
- 全同步复制,事务方式执行,主节点先写入,然后让所有slave写,必须要所有 从节点 把数据写完,才返回写成功,这样的话会大大影响写入的性能
- 半同步复制,只要有一个salve写入数据,就算成功。(如果需要半同步复制,主从节点都需要安装semisync_mater.so和 semisync_slave.so插件)
- GTID(global transaction identities)复制,主库并行复制的时候,从库也并行复制,解决主从同步复制延迟,实现自动的failover动作,即主节点挂掉,选举从节点后,能快速自动避免数据丢失。
数据库索引
- 索引记录的是索引字段信息+对应的磁盘地址,以便于快速查找
innodb的索引:聚集索引 非聚集索引(又叫二级索引,普通索引)
- 有主键,主键会作为聚集索引
- 无主键,第一个非空的唯一索引作为聚集索引
- 没有合适的唯一索引,默认创建row id 来作为聚集索引
- 聚集索引的作用: 优化查询、插入和其他数据库操作的性能
聚集索引直接指向数据对应的页
,所以节省io
二级索引和聚集索引的关系:二级索引中其实包含主键信息,通过主键值到聚集索引中找到对应的行数据。所以PK其实越小越好
在MYISAM内部,其实B+Tree中维护的叶子节点,也全部都是数据文件的地址信息,根据MYI中的index的地址,然后查询.MYD。主键索引和辅助索引都是这种形式