innodb后台进程说明

mysql 5.6 optimizer_trace 查看执行计划选择的过程
MVCC

    innodb的多版本使用undo&回滚段来构建
    innodb是聚集索引组织表,每个行记录有3个额外属性:ROW_ID、TRX_ID、ROLL_PTR
    undo记录了更改前的数据镜像,若事务未提交,对隔离级别大于等于read commit的其他事务,它们不应该看到已修改(未提交)的数据,而应一致读取老版本的数据
    在修改聚集索引记录时,总是同时存储了ROLL_PTR和TRX_ID,可通过该ROLL_PTR找到对应的undo记录,通过TRX_ID来判断该记录的可见性
    当旧版本记录中的TRX_ID指示对当前事务不可见时,则继续向前(更新的TRX_ID)构建,知道找到一个可见的记录。
    innodb在表空间中保存行的旧版本信息,这些信息被保存在回滚段中
    事务标识符(TRX_ID)指示最后插入或更新这个行的事务标识符,删除标志也被认为是一个更新,因为它在提交前指示在杭商做了一个标记。
    回滚指针(DB_ROLL_PTR)指向一个由回滚段写入的undo日志记录。如果一个行被更新了,undo日志记录包含了重建这行更新前的信息的一些必要数据
    回滚段中的undo日志分为插入日志(主要是insert操作)和更新日志(包含update和delete)。插入日志仅在事务回滚的时候有用,事务提交之后就可以马上删除掉。更新日志在一致性读的时候需要使用,如果当前没有事务再可能使用回滚段中的记录的时候,这些记录就可以删除掉了。因此,最好以适当频率提交事务,否则innodb不能删除掉过期更新日志,回滚段越来越大。
    回滚段中undo日志记录的物理大小要比其对应的插入或者更新的行要小(只记录修改的列,不记录完成的列)
    当删除某一行时,改行并不会马上从数据库的物理文件上移除,只有当innodb可以清除更新日志记录的时候,那些行机器对应的索引记录才会真正从物理上删除掉,这个清楚操作成为purge。purge以前需要由主线程来掉地,现在5.6版本已经分离出来了

redo

    ib_logfile文件个数由innodb_log_files_in_group配置决定(至少>=2),文件名序号从0开始,从ib_logfile0到ib_logfileN
    文件为顺序写入,循环使用,当达到最后一个文件末尾时,会从第一个文件开始顺序复用,redo文件切换时,会执行一次checkpoint(刷redo log,刷dirty page)
    实例重启的过程中,实例关闭以后(正常关闭,不是崩溃),redo是可以删掉的,实例开启之后会重新初始化redo,但是undo不能删
    redo log用于记录事务操作变化,记录的是数据被修改之后的值(undo记录的是数据被修改之前的值)
    不会记录临时表空间上的变化(mysql 5.7起开始有独立的临时表空间)
    redo记录的是逻辑操作,类似binglog,不像oracle是块记录。它需要应用到一个正确的page上面,如果该page本身被破坏了,则无法恢复出正确的数据,所以需要用到double write buffer
    会先放在log buffer(innodb_log_buffer_size)中,而不是立即写磁盘
    LSN:log sequence number,递增的证书,表示redo总字节数
    每次写盘后是否flush,由参数innodb_flush_log_at_trx_commit控制
    从5.5开始,redo的大小不再影响crash recovery的耗时,只影响checkpoint频率,设置较大值可减少IO消耗

redo log buffer刷新条件

    master thread每秒进行刷新
    redo log buffer使用大于1/2进行刷新
    事务提交时进行刷新
        innodb_flush_log_at_trx_commit={0|1|2}

    设置innodb_flush_log_at_timeout(默认1秒)

    innodb_flush_log_at_trx_commit
        0,事务提交时不将redo log buffer写入磁盘
        1,事务提交时将redo log buffer写入磁盘
        2,事务提交时将redo log buffer些人操作系统缓存
        通常建议设置为1,并且设置sync_binlog=1,以保证数据可靠性(双1)
    innodb_log_buffer
        通常8-32M就足够了
    innodb_log_file_size
        一般设置为512M-4G
    innodb_log_files_in_group
        至少两个文件

redo log和binlog的区别

    redo是物理逻辑日志,binlog是逻辑日志
    redo是发起时间顺序存储,而binlog是按事务提交时间顺序存储
    redo log file循环使用,binlog每次新增一个文件
    binlog更像oracle里的redo归档


undo

    用于实现MVCC以及回滚
    当我们对记录做了变更操作时就会产生undo记录,记录变更前的旧数据
    undo记录中存储的是老版本的数据,当一个旧的事务需要读取老版本数据时,为了能读到老版本的数据,需要顺着undo链找到满足可见性的记录。当版本链很长时,通常可以认为这个是比较耗时的操作。
    undo记录默认被记录到系统表空间(ibdata*)中,但是从5.6开始,也可以使用独立的undo表空间
    大多数对数据的变更操作包括insert/delete/update
    其中insert操作在事务提交前只对当前事务可见,因此产生的undo日志可以在事务提交后直接删除,归类为insert_undo
    而对于update/delete则需要维护多版本信息,在innodb里,update和delete操作产生的undo日志被归为一类,即update_undo
    MySQL5.7在undo上面的变化
        innodb_undo_logs,rollback seg数量,比如将2G的undo tablespace,切分成多少分rollback seg。默认128个,实例初始化后不可再修改。每个undo log seg可以最多存放1024个事务
        innodb_undo_tablespaces,undo log文件数,每个文件默认10MB,数量默认0,最大95个,最小2个,因为在truncate一个undo log文件时,需要保证另外一个是可用的,这样就无需停止业务了
        innodb_max_undo_log_size,控制最大undo tablespace文件的大小,超过这个值尝试truncate undo logs,truncate后的undo logs大小默认恢复为10M
        innodb_purge_rseg_truncate_frequency,用于控制purge回滚段的频率,默认128,表示purge undo轮询128次后,进行一次undo的truncate
        innodb_max_purge_lag = 10000 (如果值不为0,当目前所有未purge的undo,如果超过10000,那么新发生的DML都会被阻塞一小段时间)innodb_max_purge_lag 最好设置为0(即默认值)
        innodb_max_purge_lag_delay = 10000(每10秒至少做一次purge)
        以上两个参数的案例,可以参考老叶茶馆的文章《是谁,把innodb表上的DML搞慢的?》
    MySQL5.7之后
        支持在线truncate不用的undo logs
        set global innodb_undo_log_truncate = 1,手工truncate undo log
        当undo超过innodb_max_undo_log_size时进行truncate
        总结:三种truncate操作:手工,128次,超过max size
    show engine innodb status 里的history list length
        已提交事务,但未purge的update undo log,也就是等待purge的undo log的大小

事务在redo log、binlog中的逻辑过程

a)事务写入redo log buffer
b)将log buffer刷新到redo log,不过会先写TRX prepare标记
c)写binlog
d)在redo log写入TRX commit标记
e)将写binlog成功的标记写入redo log

    若binlog写入完成,则主从库都会正常完成事务;binlog没有写入,则主从库都不会完成事务。不会出现主从不一致的问题,除非trx_commit=0/2才有这个风险
    slave上master&relay info repository必须是TABLE,且设置relay_log_recovery=1,另外master那边设置双1,才能保证主从数据一致性。

innodb后台线程

默认有15个
- master thread(1个)
- IO thread
- read/write thread(8个,读写默认各4个)
- insert buffer thread(1个)
- log io thread(1个)
- lock monitor thread(1个)
- error monitor thread(1个)
- purge thread(1个)
- page cleaner(flushing) thread(1个)
- MySQL5.6起,master thread的工作已被大大减轻,purge,page clean等成独立线程了
后台线程

    master thread(主线程)的线程优先级别最高
    其内部几个循环(loop)组成:主循环(loop),后台循环(background loop),刷新循环(flush loop),暂停循环(suspend loop)
    会根据数据运行的状态在loop,background loop,flush loop和suspend loop中进行切换
    loop成为主循环,因为大多数的操作都在这个循环中
    loop循环通过thread sleep来实现,这意味着所谓的每秒一次或10秒一次的操作时不精确的
    在负载很大的情况下可能会有延迟

master thread

    master thread
    2个循环
        每秒要做的事
        每10秒要做的事

    如何查看:
        5.6以后select * from performance_schema.threads limit 10;
        5.6以前show engine innodb status,从background thread里面查看
        这里写图片描述

    每秒要做的事
        刷新dirty page到磁盘
        执行insert buffer merge(change buffer)
        刷redo log buffer到磁盘
        checkpoint
        检查dict table cache,判断有无需要删除table cache对象
    每10秒要做的事
        刷新dirty page到磁盘
        执行insert buffer merge
        刷redo log buffer到磁盘
        undo purge
        checkpoint
    实例关闭时
        刷redo log到磁盘
        insert buffer merge
        刷redo log buffer 到磁盘
        执行checkpoint
    优化建议
        避免dirty page堆积,适当调整innodb_max_dirty_pages_pct(<=50)
        避免undo堆积,调整innodb_max_purge_lag/innodb_max_purge_lag_delay/innodb_purge_batch_size
        及时checkpoint,调整innodb_flush_log_at_trx_commit/innodb_adaptive_flushing/innodb_adaptive_flush_lwm/innodb_flush_neighbors/innodb_flush_avg_loops
        保持事务持续平稳提交,不要瞬间大事务,或者高频率小事务

checkpiont

    定期确认redo log落盘,避免数据丢失,并提高crash recovery效率
    buffer pool脏数据太多,把脏页刷新到磁盘,释放内存
    redo log快用完了,把脏页刷新到磁盘
    redo log切换时,需要执行checkpoint

---
LOG
---
Log sequence number 693064238
Log flushed up to   693064238
Pages flushed up to 693064238
Last checkpoint at  693064238
Max checkpoint age    651585393
Checkpoint age target 631223350
Modified age          0
Checkpoint age        0
0 pending log writes, 0 pending chkp writes
8 log i/o's done, 0.00 log i/o's/second

checkpoint两种方式

    sharp checkpoint
        将所有脏页都刷新回磁盘
        刷新时系统hang住
        比较暴力,只有在需要干净重启是才需要
        innodb_fast_shutdown = 0
        0,slow,full purge,insert buffer merge(也就是不允许fast shutdown,要求做完整的关闭操作)
        1,默认,fast,skip these operation
        2,flush logs,cold status,like crashed
    fuzzy checkpoint
        持续将脏页刷新回磁盘
        对系统影响较小,但可能刷新较慢,会有迟滞
        innodb_max_dirty_pages_pct = 75
        innodb_max_dirty_pages_pct_lwm = 0

问:什么情况下innodb_fast_shutdown要设为0(保证数据安全)
答:
1. 实例升级版本
2. 主从切换
3. 实例迁移
4. 物理关机
page cleaner(flushing)(脏页的刷新)

    将脏页刷新落地到硬盘
    有两种方式
        LRU Flushing,基于LRU_list(基于最后访问时间的排序)的刷新顺序
        Adaptive Flushing,基于Flush_list(严格按照最后修改时间的顺序,LSN)的刷新顺序,innodb_adaptive_flushing = 1
    扫描列表,并找到邻居页面(innodb_flush_neighbors = 1,机械盘适用,SSD盘可关闭),一起刷新
    刷新过程
        将脏页拷贝到double write buffer
        刷新double write buffer到文件
        同步double write buffer到磁盘
        写数据文件
        同步刷数据文件到磁盘,确保落地

undo purge

    简单说,就是GC(garabge collection)
    purge都做啥
        删除辅助索引中不存在的记录
        删除已被打了delete-mark标记的记录
        删除不需要的undo log
    从5.6开始,将purge thread独立出来
        –innodb_purge_threads = 1
        –innodb_max_purge_lag = 0
        –innodb_purge_batch_size = 300
    案例:删除大量旧数据后,统计min(pkid)很慢

insert buffer/change buffer

    将非唯一辅助索引上的IUD操作从随机变成顺序IO,提高IO效率
    官方测试号称约提高15倍
    工作机制
        先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入
        若不在,则先放入到一个change buffer对象中
        change buffer也是棵树,B+树
        每次最多缓存2k的记录
        当读取辅助索引页到缓冲池,将insert buffer中该页的记录合并到辅助索引项
    –innodb_change_buffer_max_size(这个是百分比,默认25%,表示最多有25%的buffer pool用来做change buffer)

    –innodb_change-buffering(默认all,即包括insert update delete,none就是什么都不做,此外还有insert,update,delete)
        1.fast shutdown不进行insert buffer合并
        2.insert buffer进行合并插入时,tps会受影响
        3.insert buffer占用一部分buffer pool,如果辅助索引不多,可以考虑关闭或调低insert buffer

    show engine innodb status查看insert buffer相关内容

-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 1289, seg size 1291, 316623 merges
merged operations:
 insert 249806, delete mark 1123127, delete 85482
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 2365399, node heap has 3863 buffer(s)
Hash table size 2365399, node heap has 2606 buffer(s)
Hash table size 2365399, node heap has 3849 buffer(s)
Hash table size 2365399, node heap has 2100 buffer(s)
Hash table size 2365399, node heap has 2409 buffer(s)
Hash table size 2365399, node heap has 6709 buffer(s)
Hash table size 2365399, node heap has 1118 buffer(s)
Hash table size 2365399, node heap has 3659 buffer(s)
37515.35 hash searches/s, 4553.64 non-hash searches/s

注释:
size 1 =>正在使用的page
free list len => 空闲的page
seg size =>总的insert buffer page数量size + free list len + 1
insert buffer的效果 = merges / (insert + delete mark + delete)
double write,双写

    目的/作用:保证数据写入的可靠性(防止数据页损坏,又无从修复)
    因为innodb有partial write问题
        16k的页只写入了部分数据时发生crash
        redo里记录的是逻辑操作,不是物理块,无法通过redo恢复

    怎么解决partial write问题
        双写,double write
        2个1M的空间,共2M(既有磁盘文件,也有内存空间)
        页在刷新时首先顺序的写入到double write buffer
        然后再刷新回磁盘
        在可以保证原子写的硬件设备或文件系统下,可以被关闭
        slave上也可以关闭
        double write写入是顺序的,性能损失很小(SSD设备上损失则比较大)
        MySQL5.7起,采用PCIe SSD设备是会自动判断,是否要关闭double write buffer
        先写double buffer,再写磁盘,如果double write写失败了,那么肯定没写入磁盘
        这里写图片描述

    double write的状态统计(mysqladmin ext | grep -i dbl)
        innodb_dblwr_pages_written(发起多少次写的请求)
        innodb_dblwl_writes(实际写了多少次)
        理想比例是64:1,因为1M包含64个page,但是很难达到这高比例
    性能损失
        –innodb_double = 0关闭,1打开
        status
        innodb_dblwr_pages_written
        innodb_dblwr_writes

预热

    buffer pool dump& restor 启动预热
        innodb_buffer_pool_filename
        innodb_buffer_pool_dump_now
        innodb_buffer_pool_dump_at_shutdown
        innodb_buffer_pool_load_now
        innodb_buffer_pool_load_at_startup
    手工预热
        select count(*) from t force index(primary)
        select count(*) from t
        select * from t
    可以防止数据库刚开起来因为承受不了瞬间到来的物理读请求而秒崩,强烈建议开启(5.6以后才有的功能,且可以在线动态开启)。

adaptive hash index

    对buffer pool中热点索引页数据再次进行索引
    目的:缓存索引中的热点数据,提高检索效率,O(1) VS O(N)(从对B+树的搜索变为对hash的搜索)
    对热点buffer pool建立AHI,非持久化
    只支持等值查询
        idx_a_b(a,b)
        where a = xx
        where a = xx and b = xx
    AHI很可能是部分长度索引,并非所有查询都能有效果
    设置innodb_adaptive_hash_index = 0 关闭
    设置innodb_adaptive_hash_index_parts 使用AHI分区/分片降低竞争提高并发
    个别场景下,开了AHI后,可能导致spin_wait lock 比较大,可以关闭掉
    评估自适应hash索引的作用(show engine innodb status)

-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 1289, seg size 1291, 316623 merges
merged operations:
 insert 249806, delete mark 1123127, delete 85482
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 2365399, node heap has 3863 buffer(s)
Hash table size 2365399, node heap has 2606 buffer(s)
Hash table size 2365399, node heap has 3849 buffer(s)
Hash table size 2365399, node heap has 2100 buffer(s)
Hash table size 2365399, node heap has 2409 buffer(s)
Hash table size 2365399, node heap has 6709 buffer(s)
Hash table size 2365399, node heap has 1118 buffer(s)
Hash table size 2365399, node heap has 3659 buffer(s)
37515.35 hash searches/s, 4553.64 non-hash searches/s

注:AHI的作用 hash searches / (hash searches + non-hash searches)
crash recovery

    redo,redo前滚结束后,server开始对外提供服务,后面的过程放在后台线程继续工作
    当实例从崩溃中恢复,需要将活跃的事务从undo中提取出来,对于ACTIVE状态的事务直接回滚,对于prepare状态的事务,如果该事物对应的binlog已经记录,则提交,否则回滚事务
    change buffer merge
    purge
    xa recover

加快crash recovery速度

    升级到5.5以后的版本
    提高IO设备性能
    适当调低innodb_max_dirty_pages_pct,50以下
    设置innodb_flush_log_at_trx_commit = 1,让每个事务尽快提交,避免有其他事务等待,产生大量的undo,增加purge工作量
    5.7又进一步改进,crash recovery时无需扫描所有数据文件并创建内存对象(数据文件巨多时会产生严重性能问题),而只检查checkpoint+那些标记为被修改过的文件,从一个checkpoint点开始,可以找到所有崩溃恢复需要打开的文件,从而避免扫描数据目录

innodb引擎重点参数

    innodb_buffer_pool_size
        最大的内存块,建议为物理内存的50-80%
    innodb_max_dirty_pages_pct
        buffer pool中dirty page最大占比,建议不超过50%
    innodb_old_blocks_pct
        buffer pool中old block sublist最大占比,默认3/8
    innodb_change_buffering
        change buffer 类型,ALL或其他
    innodb_log_buffer_size
        redo log buffer,能缓存5秒左右产生的redo就够,32MB基本管够
    innodb_sort_buffer_size???(后续研究)
        innodb往表中批量加载数据更新索引。以及Online DDL时,将当前发生的DML记录到临时log中???。(不要只看字面意思)
    innodb_data_file_path
        共享表空间初始大小,建议至少1G以上
    innodb_log_file_size
        redo log大小,加大有助于减小checkpoint频率,提高tps
    innodb_flush_log_at_trx_commit
        redo log刷新机制,1最安全,0性能最好,2折中
    innodb_io_capacity
        innodb 后台线程最大iops上限
    innodb_flush_methon
        刷新innodb data file和log file使用方式,推荐O_DIRECT
    innodb_stats_on metadat = 0
        执行show table status / show index时是否更新统计信息
    innodb_autoinc_lock_mod = 1
        auto-inc锁模式,推荐1
    innodb_file_per_table =1
        是否启用独立表空间,推荐1。5.5开始可动态修改
    innodb_fast_shutdown = 0/1
        是否快速关闭,推荐1。需要版本升级或机器重启是,要改为0。
    innodb_force_recovery = 0
        innodb恢复级别,可选0-6,从最小开始尝试启动。默认一定要设置为0。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值