最近学习了一下percona 发布的 《Innodb Architecture and Internals》,顺道记录下,有些知识点是很熟悉的,也有些是刚知道的。。。
Innodb表空间
1. innodb表是索引的集合,数据直接保存在主索引的叶节点里,2级索引的页指向主所以
2. 索引叶节点和非叶节点使用的是不同的segment,除此之外,还有些特殊的segment(回滚段和insert buffer)
3. ib-logfile 的header保存了最新的checkpoint信息, re-do log是按record保存的而不是page,record上记录了在哪个page上做了什么
4. 5.6版本中undo表空间(回滚段)可以单独保存
innodb主要的后台线程
1. 主线程,主要负责调度 flush,pruge,checkpoint, insert buffer merge
2. IO线程: read 多线程预读
write多线程后台写
insert buffer 合并
flush log
3. purge线程: 回收资源
4. 死锁检测线程
内存
1. innodb_buffer_pool_size 实际分配的值会大于设置的值,主要是一些额外的管理或锁之类的结构的内存消耗
2. innodb_log_buffer_size 大事务较多的时候应该调大(默认8M),以减少记录日志时刷盘的次数
IO
1. 大部分的读操作都是工作线程直接做的
2. 预读操作则是由后台线程做的
3. insert buffer的合并也会导致读操作
4. 大部分的write操作是后台线程做的
5. 当没有可用的free buffer的时候,工作线程直接写
6. 日志的flush 行为由innodb_flush_log_at_trx_commit 决定
1 同步写
0 异步写,由后台每s刷一次
2 半同步,即同步写道操作系统缓存,然后1s执行一次fsync
flush write
1. 大部分写的flush操作都是按顺序的,即最老的最先
2. innodb_addptive_flushing 默认为on,即按当前负载决定flush的时候刷几个page
3. 当数据大于memory的时候可能发生LRU flush,该flush由工作线程去做 (当无法在LRU链表的后10%中找不到干净页)<这个还不了解,需要再研究>
page checksums
1. 读的时候进行校验,写的时候更新checksums,可以用innodb_checksums=0来关闭
double write buffer
1. 因为mysql的一个page的大小比操作系统一个page大,所以可能出现 partial write 而无法恢复(写一半crash了)。使用double wirte buffer 可以避免无法恢复的情况。 这个buffer实在表空间的开始部分,而不是在内存中,写磁盘的时候会先写道double write buffer, 然后后面在从double wirte buffer 刷到对应的位置(这个过程会进行合并优化,尽量保证刷的时候是顺序写而不是随机写,以减少对性能的影响)。
2. 开启double write buffer可以显著减少redo日志的大小。
3. innodb_flush_method = O_DIRECT 关闭
索引
1. 主键不宜太长
2. 主键直接包含数据
3. 2级索引指向主键
4. 自增列做主键效率比较高
隔离
1. 可串行化: 锁read并跳过多版本
2. 可重复读: 从事务开始的时候读已经提交的事务
3. 读提交: 在语句开始的时候读已经提交的事务
4. 读未提交: 完全没隔离。。。
5. 多版本只是对读而言,写只会操作最新版本,并且多版本的没有数量限制
6. 锁读的效率很差,如select for update 由于读的时候还要去上锁,所以代价较高,速度较慢
7. 最新的数据在page里,老的在回滚段里(多版本)
8. 回滚段中的版本是使用链表串接的,越老的越深,所以如果大事务的话可能会读很深的老版本从而影响性能
9. 索引会覆盖所有的版本,即老版本上也有索引,并且有版本区分
锁
1. innodb_lock_wait_timeout 行锁等待的超时时间
2. mysql使用的是悲观锁。 除了传统的S,X锁以外还有意向锁 IS IX
3. innodb锁行和间隙(gap), 这主要是为了一致性读的实现
4. 每个有锁的page都有一个bitmap用于记录page中每个行的锁状态
latching(innodb内部锁)
1. 可通过show engine innodb status来查看
2. innodb_sync_spin_loops innodb锁的等待时间
3. 可以使用 innodb_thread_concurrency来限制并发的线程数来降低锁冲突
页调度(buffer pool中的数据与索引)
1. 使用LRU算法从LRU链表的尾部调出 clean的page
2. 若找不到clean page 工作线程直接写盘
写checkping
1. 如果log中没有没有足够的剩余空间,那么事务需要等,直到可以完整地刷到log中。
Innodb表空间
1. innodb表是索引的集合,数据直接保存在主索引的叶节点里,2级索引的页指向主所以
2. 索引叶节点和非叶节点使用的是不同的segment,除此之外,还有些特殊的segment(回滚段和insert buffer)
3. ib-logfile 的header保存了最新的checkpoint信息, re-do log是按record保存的而不是page,record上记录了在哪个page上做了什么
4. 5.6版本中undo表空间(回滚段)可以单独保存
innodb主要的后台线程
1. 主线程,主要负责调度 flush,pruge,checkpoint, insert buffer merge
2. IO线程: read 多线程预读
3. purge线程: 回收资源
4. 死锁检测线程
内存
1. innodb_buffer_pool_size
2. innodb_log_buffer_size 大事务较多的时候应该调大(默认8M),以减少记录日志时刷盘的次数
IO
1. 大部分的读操作都是工作线程直接做的
2. 预读操作则是由后台线程做的
3. insert buffer的合并也会导致读操作
4. 大部分的write操作是后台线程做的
5. 当没有可用的free buffer的时候,工作线程直接写
6. 日志的flush 行为由innodb_flush_log_at_trx_commit 决定
flush write
1. 大部分写的flush操作都是按顺序的,即最老的最先
2. innodb_addptive_flushing 默认为on,即按当前负载决定flush的时候刷几个page
3. 当数据大于memory的时候可能发生LRU flush,该flush由工作线程去做 (当无法在LRU链表的后10%中找不到干净页)<这个还不了解,需要再研究>
page checksums
1. 读的时候进行校验,写的时候更新checksums,可以用innodb_checksums=0来关闭
double write buffer
1. 因为mysql的一个page的大小比操作系统一个page大,所以可能出现 partial write 而无法恢复(写一半crash了)。使用double wirte buffer 可以避免无法恢复的情况。 这个buffer实在表空间的开始部分,而不是在内存中,写磁盘的时候会先写道double write buffer, 然后后面在从double wirte buffer 刷到对应的位置(这个过程会进行合并优化,尽量保证刷的时候是顺序写而不是随机写,以减少对性能的影响)。
2. 开启double write buffer可以显著减少redo日志的大小。
3. innodb_flush_method = O_DIRECT 关闭
索引
1. 主键不宜太长
2. 主键直接包含数据
3. 2级索引指向主键
4. 自增列做主键效率比较高
隔离
1. 可串行化:
2. 可重复读: 从事务开始的时候读已经提交的事务
3. 读提交: 在语句开始的时候读已经提交的事务
4. 读未提交: 完全没隔离。。。
5. 多版本只是对读而言,写只会操作最新版本,并且多版本的没有数量限制
6. 锁读的效率很差,如select for update
7. 最新的数据在page里,老的在回滚段里(多版本)
8. 回滚段中的版本是使用链表串接的,越老的越深,所以如果大事务的话可能会读很深的老版本从而影响性能
9. 索引会覆盖所有的版本,即老版本上也有索引,并且有版本区分
锁
1. innodb_lock_wait_timeout
2. mysql使用的是悲观锁。 除了传统的S,X锁以外还有意向锁 IS IX
3. innodb锁行和间隙(gap), 这主要是为了一致性读的实现
4. 每个有锁的page都有一个bitmap用于记录page中每个行的锁状态
latching(innodb内部锁)
1. 可通过show engine innodb status来查看
2. innodb_sync_spin_loops
3. 可以使用 innodb_thread_concurrency来限制并发的线程数来降低锁冲突
页调度(buffer pool中的数据与索引)
1. 使用LRU算法从LRU链表的尾部调出 clean的page
2. 若找不到clean page 工作线程直接写盘
写checkping
1. 如果log中没有没有足够的剩余空间,那么事务需要等,直到可以完整地刷到log中。