Innodb学习(一)

InnoDB

后台线程

master thread

io thread

4四种io线程类型:insert buffer thread,log thread,read thread,write thread
innodb使用了大量异步io处理写io请求
io thread的工作是负责这些io请求的回调处理

purge thread

回收已使用的、不再需要的undo页

page cleaner thread

刷脏页

内存

缓存池

磁盘读到的页放入缓存池,下次读取相同页时先判断缓存中是否存在
页的修改,先修改缓存池中的页,再以一定频率刷回磁盘。不是每次页更新时就刷盘,而是通过checkpoint机制
缓存的数据页类型
索引页,数据页,undo页,insert buffer,自适应哈希索引,锁信息,数据字典信息

内存区域的管理

LRU List:lru算法,midpoint,最近访问的页放入到lru列表的midpoint位置,默认是lru列表的5/8处

为啥不用朴素lru算法?
防止某些sql操作可能会使缓存池中的页被刷出lru列表,影响缓存池效率。
如索引或数据的扫描操作。这些扫描到的页通常仅在一次查询中需要,不是活跃的热点数据

unzip_LRU列表
伙伴算法
Free List:
Flush List

redo log_buffer

redo日志信息先放在这里,再按一定频率刷新到重做日志文件

什么时候刷redolog buffer
1、master thread每秒刷一次
2、每个事务提交时刷新
3、redolog buffer剩余空间小于1/2时刷新

额外内存池

在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓存池中申请

checkpoint技术

页操作是先在缓存池中进行的,如果刷脏页的时候宕机,数据丢失怎么办?
采用write ahead log策略,事务提交时,先写redolog,再修改页。发生宕机而导致数据丢失时,通过redolog完成数据的恢复

insert buffer

针对二级、非唯一索引
1、为啥针对二级索引
按主键插入通常是顺序插入,速度较快。而二级索引的插入通常是离散的,随机读写导致插入性能下降

2、为啥是非唯一索引
按唯一索引插入记录时需要到磁盘查找页验证唯一性,缓存就失去了意义

3、insert buffer怎么工作的
插入非唯一二级索引时,先判断索引页是否在缓存池里,在的话直接插入。否则先放入到一个insert buffer对象中,数据库会以为这个索引已经插入到叶子节点了,实际还没有。然后以一定的频率进行insert buffer与二级索引页子节点的merge操作,通常可以将多个插入合并到一个操作中(因为在同一索引页)

内部实现

insert buffer的数据结构是一棵全局B+树,负责对所有表的二级索引进行insert缓存。这棵B+树存放在共享表空间,默认在ibdata1

insert buffer非叶子节点
存放查询的search key,包括表id,maker(兼容老版本insert buffer),offset页所在偏移量

insert buffer叶子节点
表id,marker,offset,metadata(记录进入insert buffer的顺序),二级索引记录…

merge

change buffer

insert buffer的升级,还是针对非唯一二级索引

可以对DML操作都进行缓存:insert,delete,update,分别是insert buffer,delete buffer,purge buffer

对一条记录进行update的过程
1、将记录标记为已删除(对应delete buffer)
2、将记录物理删除(对应purge buffer)

double write

索引与算法

innodb支持的索引类型

自适应哈希

innodb根据表的使用情况自动为表生成哈希索引,不能人为干预

B+树

插入操作:
1、叶子节点的页未满,直接将记录插入
2、叶子节点页已满,其父index节点页未满,则拆分叶子节点,将中间节点放入index页节点,原中间节点两边的记录拆成两个叶节点页,小于中间节点的放左边,大于中间节点的放右边
3、叶子节点页已满,其父index页节点也已满。先拆分叶节点,中间节点上移后,接着拆分父index节点,如果再上一层的index节点还是满的,继续拆分

删除操作:
1、删除根据填充因子操作。填充因子可设置的最小值为50%。当叶节点页填充因子大于设定值,直接删记录。如果记录同时是index页节点的记录,用叶节点页中被删节点的右边相邻记录代替
2、叶节点页填充因子小于设定值:合并叶节点页和其兄弟节点,并更新index页
3、叶节点页和index页填充因子都小于设定值:合并叶节点和其兄弟节点;更新index页;合并index页和它的兄弟节点

连续插入时的页分裂
page header保存了插入的顺序信息,如果往同一方向连续插入了多条记录,则会向某一侧分裂,提高页空间的利用率

聚簇索引

叶节点页存放整张表的行数据记录

聚簇索引的存储在物理上可以不连续,只是逻辑上连续。页通过双向链表链接,页按照主键顺序排序;每个页中的记录通过双向链表进行维护

二级索引

叶节点页存放主键值以及指向主键数据页的偏移量

通过二级索引查其他行数据,需要回表操作

online ddl
索引的使用
联合索引,最左匹配
覆盖索引

从二级索引树就可以得到查询的记录时,就不回表查询了

优化器不使用索引的情况
索引提示
mutirange read优化

MRR工作方式:
1、根据二级索引查记录,将记录放在缓存(这时的记录是按二级索引键值排序的)
2、将缓存中的记录按主键排序
3、顺序地回表查询完整的行数据

ICP,索引下推

没有ICP的版本:
根据索引查找记录,将记录都放入缓存,再根据where条件过滤记录

ICP的过程:
根据索引查记录,取出索引的同时直接判断是否满足where条件,不满足的就不取去了

全文索引

倒排索引
利用一张辅助表存储了单词与单词自身在一个或多个文档中所在位置之间的映射,通过有两种形式:
inverted file index:{单词,单词所在文档的id}
full inverted index:{单词,(单词所在文档id,在具体文档中的位置)}

innodb采用full inverted index的方式。将(document id,position)视为一个ilist。全文检索表中有两列,word字段和ilist字段,且在word字段上设有索引。为了提高并行性能,innodb共有6张辅助表,每张表根据word的Latin编码进行分区

FTS index cache  全文检索索引缓存
全文检索查询语法

innodb引擎中的锁

锁类型

共享锁
排他锁
意向共享锁
意向排他锁

一致性非锁定读

是指innodb存储引擎通过行多版本控制的方式读取当前执行时间数据库中行的数据。如果读取的行正在执行修改操作,这时读取操作不会等待行锁释放,而是直接去读行的一个快照数据

非锁定读机制提高了数据库的并发性,是innodb默认的读取方式

读提交隔离级别下的非一致性读总是读取被锁定行的最新快照数据

可重复读隔离级别下非一致性读总是读取事务开始时的行数据版本

一致性锁定读

一致性锁定读有两种方式:

select … for update
select … lock in share mode

前者对读取的行记录加X锁,后者对读取的行记录加S锁
一致性锁定读语句必须在一个事务中,事务提交时锁随之释放

自增长与锁
外键与锁

锁的算法

行锁三种算法

record lock
gap lock,锁住一个范围,不包括记录本身
next-key lock:gap lock+record lock,锁定一个范围加记录本身

查询的索引含有唯一属性时,next-key lock降级为record lock??

锁问题

脏读
不可重复读
幻读
丢失更新(应用逻辑上的丢失,比如事务1查询余额(2000),将余额放入本地内存,并显示给用户1;然后事务2也查询同一账户,并显示给用户2;用户1转账1000给其他人,更新数据库并提交;用户2转账1元给其他人,更新数据库并提交。此时账户余额为1999,前面的更新丢失。
怎么处理?让事务的操作串行化

阻塞

死锁

锁升级

innodb不存在锁升级问题,因为其不是根据每个记录来产生行锁,而是根据每个事务访问的每个页对锁进行管理,采用位图的方式。一个事务锁住页中的一个记录还是多个记录的开销通常一致

事务

事务类型

1、扁平事务:所有操作都处在同一层次
限制是不能提交或回滚事务的某一部分,或分几个步骤提交
2、带有保存点的扁平事务:允许在事务执行过程中回滚到同一事物中较早的一个状态。保存点用来通知系统应该记住事务当前的状态,以便发生错误时事务能回滚到保存点当时的状态
save work 函数建立保存点

3、链事务:提交一个事务时,释放不需要的对象,将必要的处理上下文隐式地传递给下一个要开始的事务。提交事务操作和开始下一个事务操作合并为一个原子操作

4、嵌套事务:是由若干事务组成的一棵树,子树可以是嵌套事务或扁平事务;处在叶节点的是扁平事务;根节点事务称为顶层事务;子事务可以提交或回滚,但操作不立刻生效;树中任一事务的回滚会引起它的所有子事务一起回滚

5、分布式事务

事务的实现

事务隔离性由锁实现。原子性、一致性、持久性由redolog,undolog完成,redolog保证事务的原子性和持久性,undolog保证事务的一致性

redolog

redolog用来实现事务的持久性

由两部分组成:内存中的redolog buffer,是易失的;重做日志文件redolog file,是持久化的

为确保每次日志都写入redolog file,在每次将redolog buffer写入redolog file后,innodb都需要调用一次fsync操作

redolog block

redolog block:redolog以512字节大小的块为单位进行保存,由于磁盘扇区大小也是512字节,因此redolog的写入可以保证原子性,不需要双写技术;redolog block中日志块头、日志块尾各占12、8字节。redolog buffer由redolog block组成,redolog buffer像一个数组,redolog block的日志头中的log_block_hdr_no标记在block在数组中的位置,占四字节,第一位表示flush bit,因此最大值为2G

redolog group

重做日志组redolog group:包含多个重做日志文件,innodb实际只有一个redolog group
redolog buffer刷盘时机:
事务提交时;
logbuffer中有一半的内存空间已经被使用时;
logcheckpoint时

redolog追加写入在redolog file的最后部分,一个redolog file被写满时,会接着写入下一个redolog file,使用方式为round-robin

对redolog file的写入都是顺序的吗?
不是。每个redolog file前4个块(2kB)不保存logblock的信息。loggroup的第一个redolog file的第一个块存的是logfile header,第二第四个块存checkpoint1,checkpoint2.其他redolog file仅保留这些空间,不存信息。redolog file的写入不仅包括redolog block的写入,也包括第一个redolog file前2kB部分的更新(跟恢复操作有关)

redolog格式

格式是基于页的。

redolog_typespacepage_noredolog body

头部是通用,包括
1.redolog_type
2.space表空间id
3.page_no页的偏移量

redolog body根据redolog类型的不同有不同的存储内容

LSN

LSN,Log Sequence Number,代表日志序列号,在innodb中占8字节,单调递增,有如下含义:
1.redolog写入的总量
2.checkpoint的位置
3.页的版本

每个页的头部有一个值FIL_PAGE_LSN记录了该页的LSN,表示该页最后刷新时LSN的大小,用来判断页是否需要进行恢复操作

show engine innodb status命令可以查看LSN的情况,3个重要值:
1.log sequence number当前的lsn
2.log flushed up to刷新到redolog file的lsn
3.last checkpoint at刷新到磁盘的lsn
其中2和3啥区别?
后者似乎可能会更小一点

恢复

redolog记录的是物理日志,恢复速度比binlog快
只需要恢复从checkpoint开始的日志
redolog是物理日志,因此是幂等,即多次应用同一条日志,结果都相同

undolog

undolog用于支持逻辑回滚(所有修改都被逻辑地取消,但数据结构和页本身在回滚之后可能不太相同)
undolog存放在undo段,位于共享表空间

undolog另一个作用是MVCC。用户读取某行记录时,如果记录被其他事务加锁占用了,当前事务可以通过undo读取之前的行版本信息,实现非锁定读取

undolog会产生redolog,因为undolog也需要持久性的保护

存储管理

采用段的方式进行管理,可以设置innodb中rollback segment的数量,以及每个rollback segment中undolog segment的数量。

事务在undolog segment分配页并写入undolog的这个过程同样需要写入redolog。事务提交时,innodb会干两件事:
1.将undolog放入列表,以供之后的purge操作
2.判断undolog所在的页是否可重用,若可用则分配给下个事务使用

事务提交后不能马上删掉undolog及其所在的页,其他事务还有可能需要通过undolog获得行记录之前的版本。事务提交时将undolog放入一个链表中,是否可以最终删除undolog及其所在页由purge线程判断

由于事务提交时并不能马上释放页,若为每个事务分配一个undo页,假设应用的增删改tps为1000,则一分钟就需要6w个页,大约需要1G的存储空间,若每秒只能purge20页,这样的设计对磁盘空间要求太高。因此undo页可以被重用。

当事务提交时先将undolog放入链表,然后判断undo页的使用空间是否小于3/4,若是则该页可重用,之后新的undolog记录在当前undolog的后面,由于存undolog的列表是以记录进行组织的,一个undo页可能存放不同事务的undolog,因此purge会涉及磁盘的离散读取,是一个比较缓慢的过程

show engine innodb status中的transactions里的history list length就代表undolog的数量

undolog格式

分为insert undolog和update undolog

insert undolog是在insert操作中产生的,由于insert操作的记录只对事物本身可见,因此undolog可以在事务提交后直接删除,不需要purge

update undolog记录了对delete和update操作产生的undolog。这种undolog可能需要提供MVCC机制,因此事务提交时不能直接删除,而是放入undolog链表,等待purge线程进行最后的删除

update undolog比insert undolog记录的内容更多,占用空间更大

purge

delete操作只是将相应记录的delete flag设置为1,记录并没有从B+树上物理删除;对相应的二级索引记录同样没有做任何处理,甚至没有产生undolog。删除记录的操作被延迟了,最终由purge操作完成

innodb有一个history列表,根据事务提交的顺序将undolog进行链接,先提交的事务总在尾端。
执行purge时,先从history list中找到第一个需要被清理的记录(trx1),清理之后在trx1的undolog所在的页中继续找可以被清理的记录,如果页中没有其他可清理的记录,再回到history list中查找。
为啥这么干?
先从history list查undolog,再从undolog所在的undopage查其他可清理的undolog,避免了大量的随机读取操作,可以提高purge的效率

group commit

事务控制语句

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值