mysql innodb存储_[MySQL]Innodb储存引擎

hash储存引擎hash散列数据结构特点是查找快时间复杂度O(1),其原理是类似array[f(x)],数值x通过函数f算法映射到数组的某个位置。由于数值逻辑上是连续的在物理上不一定连续,物理上的不连续会造成大量的随机IO,磁盘性能较低,不适合范围、排序等场景,但适用于等值查找的。

hash散列数据结构还有一个特点hash冲突。解决hash冲突常用的方法有链地址法,在数据库数据量很大的情况下冲突很明显,性能明显下降。

MyISAM储存引擎MyISAM和Innodb一样采用B+树数据结构,但前者在存储结构上采用非聚集形式,而Innodb采用聚集形式。MyISAM非聚集形式存储索引和数据,即两中数据是分开存储的。MyISAM主索引叶子结点存储的是行数据在磁盘的地址,Innodb主索引叶子结点存储的是行数据。

MyISAM不支持事务,Innodb支持事务的ACID特性,具体怎样支持的后面会讲到。

MyISAM支持表锁,不支持颗粒度更小的行锁,Innodb则支持。故对MyISAM引擎的数据库数据进行修改时,就需要表锁,这样对多并发下性能大打折扣,故MyISAM不太适合频繁的大量的数据更新的场景。

Innodb的辅助索引的叶子结点记录的是主索引值,故通过辅助索引查找到主索引值后还需要回表到主索引查询,如果都是高度为3的B+树,则共需要6次IO查询。 而MyISAM辅助索引叶子结点存的是数据的行在磁盘中的地址直接就能获取。这种情况下MyISAM更适合较多的读的场景。

Innodb储存引擎

事务(ACID)A(Atomic)-原子性

原子性是表示:事务内做的事,要么都成功,要么都失败。 如果失败了,就需要恢复到原来的状态---回滚。

回滚在Innodb中通过undo log实现(当然undo log也是后面mvcc的重要工具)

首先我们要知道一些Innodb的点,Innodb中会对每个事务进行全局性的分配唯一事务ID,存储的数据行有两列隐藏的:DB_TRX_ID:用于记录这条创建\最后一次修改的事务ID。

DB_ROLL_PTR:回滚指针,指向该条记录的上一版本(undo log文件中)。

TODO:此处应该画一张图

一般会记录insert undo log:

update\delete undo log:维护一个历史版本数据,通过DB_ROLL_PTR指针维护为链表形式,在需要回滚时直接从历史版本中回滚。MVCC也依赖于这个历史版本链,后面隔离性中会说明。

C(Consistency)-一致性

一致性是并发环境下数据库最求的最终的操作的正确的结果。其他A、I、D三项的满足才能满足一致性。I(Isolation)-隔离性

在并发环境下一般会有这几种场景:①读-读,不会对数据库改,变故无需考虑。②读-写,场景中有读写之间临界资源安全问题,可能遇到脏读,幻读,不可重复读。③写-写,场景中也有临界资源安全问题。

SQL标准把隔离划分成了几个级别用于解决上述问题:读未提交(Read Uncommitted):会出现脏读,幻读,不可重复读问题。

读已提交(Read Committed):脏读消灭了,但是幻读,不可重复读问题仍然存在。

可重复度(Repeatable Read):Innodb中默认隔离级别,SQL标准的规定在此隔离级别下幻读问题仍存在,但在innodb中有些不太一样,采用next-key lock间隙锁避免了幻读的产生。

串行化(Serializable):通过序列化方式来解决所有并发问题,当然这样做效率是很低的。

在Innodb中提出了“快照读”和“当前读”两个概念,在解决读已提交、可重复度这两个隔离级别下的“读-写”问题。

快照读:如select未加锁的读取就是,Innodb基于并发性能的考虑没采用加锁,而采用MVCC方式,读取基于历史版本信息,故我们称为快照读。 在“读已提交”、“可重复度”隔离级别中都有使用MVCC来提高读的并发性能。

MVCC(多版本并发控制),其实现依靠undo log日志。在本事务开启后会产生一个事务ID,并产生一个叫做readview的快照数据结构,包括:①当前整个系统中活跃的事务的ID集合m_ids、②活跃的事务ID的最小值min_trx_id、③当前系统即将分配下一个事务ID值max_trx_id。

TODO:此处应该有一张图

在“快照读”某行时需要明确哪些历史版本数据对当前数据可见,就需要寻着undo log版本链中DB_TRX_ID依次与活跃的事务ID进行比较,遍历时间复杂度O(n),如果某行历史链表叫长,则效率会下降:DB_TRX_ID < min_trx_id,则表示 undo log历史链表上此版本数据是在本次事务开启前就已经提交了,故对当前事务可见。

DB_TRX_ID > max_trx_id,表示undo log历史链表此版本是在本次事务开启后提交的,故对当前事务不可见。

DB_TRX_ID 是否在m_ids中存在,如果在则表示当前事务开启时对应的DB_TRX_ID这个版本的数据是在活跃中的,故对当前事务不可见;如果不在则对当前事务可见。

“快照读”在可重复读与读已提交隔离级别下快照(readview)生成规则不太一样。可重复读:是在事务一开始就生成一个readview,直到本次事务结束都只是使用这一个快照; 读已提交:表示可以读取其他事务已经提交了的数据,故在每次读取时都会生readview。

当前读:读取最新数据这是最大区别于快照读是读取历史版本数据。一般的实现就是通过加锁。

Innodb中在可重复读隔离级别下使用如:update、select … for update、select .. lock in share mode等SQL语句,通过使用了行锁+next-key lock解决了幻读(注意区分幻读与不可重复度的概念的区别,前者重点在新增和删除,后者在于修改)当然就会出现阻塞(block)情况。

在命中索引的情况下使用行锁,不然就是全表锁住。

如:SELECT * FROM table1 WHERE id >100 FOR UPDATE; 如果命中索引,就使用了行锁[100]+next-ley lock间隙锁(100,+∞]。如果没有命中索引,则会使用表锁锁住全表。

D(Durability)-持久性

持久性表示提交的事务修改是永久性的。Innodb为了保证较高的写性能,数据是缓存在Innodb buff poll中,对磁盘数据的修改后脏数据是缓存在buff pool中的,如果这时候掉电或者系统崩溃,则数据就会丢失,为了保证数据库的事务持久性和一致性,在innodb中采用redo log重做日志来保证。

事务在提交后,数据的修改提交到redo log重做日志中 ,是先于buff pool落盘,这种做法专有名词WAL(百度百科:Write-Ahead Logging)。当系统崩溃后,Innodb直接重放rdo log即可恢复。redo log是记录物理页的修改,格式一般如:xx页偏移xx字节处修改xx

且采用APPEND追加写的方式,这样在磁盘上就是顺序IO操作,这就保证比事务对数据的写(很可能不是顺序IO)更高效率的存储起来。

当然每次事务开启后对数据的修改后,①先写入redo log buff中;②在事务提交时,进行系统调用write写入内核缓冲区(page cache)中;③刷盘策略,这个策略可以选择:每次提交事务后刷盘、每次提交后不刷盘依靠周期性1秒刷盘。(这个和Redis的AOF中的刷盘策略很像)。刷盘API在先前文章中有详细介绍:越过山丘:系统调用sync的一系列​zhuanlan.zhihu.comzhihu-card-default.svg

总结

MySQL可以选择上述的索引,但需要结合业务场景需求来综合考虑,从5.5.8版本默认采用Innodb储存引擎。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值