MySQL

存储引擎

MyISAM是MySQL 5.0 之前的默认数据库引擎,最为常用。拥有较高的插入,查询速度,但不支持事务。
InnoDB是事务型数据库的首选引擎,支持ACID事务,支持行级锁定, MySQL 5.5 起成为默认数据库引擎。

InnoDB的关键属性包括:

  1. ACID事务特性支持,包括commit, rollback以及crash恢复的能力
  2. 行级别锁以及多版本并发控制MVCC
  3. 利用主键的聚簇索引(clustered index)在底层存储数据,以提升对主键查询的IO性能
  4. 支持外键功能,管理数据的完整性


事务的隔离性是通过MySQL锁机制实现 
原子性,一致性,持久性则通过MySQL的redo和undo日志记录来完成

InnoDB 多版本控制

为保证并发操作和回滚操作, InnoDB会将修改前的数据存放在回滚段(undo log)中。

InnoDB会在数据库的每一行上额外增加三个字段以实现多版本控制, 
第一个字段是DB_TRX_ID,用来存放针对该行最后一次执行insert、 update操作的事务ID,而delete操作也会被认为是update,只是会有额外的一位来代表事务为删除操作; 
第二个字段是DB_ROLL_PTR,指针指向回滚段里对应的undo日志记录; 
第三个字段是DB_ROW_ID,代表每一行的行ID。

回滚段中的undo日志记录只有在事务commit提交之后才会被丢弃,为避免回滚段越来越大,要注意及时执行commit命令


缓存池: 
buffer pool缓存池是InnoDB在内存中开辟的用来缓存表数据和索引数据的区域, 一般可以设置为50%~80%的物理内存大小, 通过对经常访问的数据放置到内存当中来加快访问速度。

Buffer pool以page页的格式组成,页之间组成list列表,并通过LRU算法(最近最少使用算法) 对长久不使用的页进行置换。

数据的读写需要经过缓存(缓存在buffer pool 即在内存中)数据以整页(16K)位单位读取到缓存中缓存中的数据以LRU策略换出(最少使用策略)IO效率高,性能好

什么是脏读、幻读、不可重复读?要怎么处理?

这些问题都是MySQL进行事务并发控制时经常遇到的问题。
脏读:在事务进行过程中,读到了其他事务未提交的数据。
不可重复读:在一个事务过程中,多次查询的结果不一致。
幻读:在一个事务过程中,用同样的操作查询数据,得到的记录数不相同。
脏写:在一事务中,查询到了一条记录,并要更新,比如某个字段+200 , 但此时其他事务提交了对这个记录的更新,就会造成脏写。 (解决方式: 1,乐观锁,加上version 字段,update 的时候条件带上当前的版本号 2,Redis 分布式锁 )
处理的方式有很多种:加锁、事务隔离、 MVCC




加锁:
1、脏读:在修改时加排他锁,直到事务提交才释放。读取时加共享锁,读完释放锁。
2、不可重复读:同上
3、幻读:加范围锁。
针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。
针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select ... for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。

事务隔离:
1、read uncommit读未提交。可能会读到其他事务未提交的数据,也叫做脏读。
2、read commit读已提交。 解决脏读,无法解决不可重复度。(Oracle 的默认级别)
3、repeatable read 可重复复读。这是mysql的默认级别,解决不可重复读,无法解决幻读。
4、serializable 串行,一般是不会使用的,他会给每一行读取的数据加锁,会导致大量超时和锁竞争的问题。

MVCC:
多版本并发控制:读取数据时通过一种类似快照的方式将数据保存下来,这样读锁就和写锁不冲突了,不同的事务 session会看到自己特定版本的数据,版本链
MVCC只在 READ COMMITTED和 REPEATABLE READ两个隔离级别下工作。其他两个隔离级别够和MVCC不兼容,因为READ UNCOMMITTED 总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE 则会对所有读取的行都加锁。
聚簇索引记录中有两个必要的隐藏列:
trx_id:用来存储每次对某条聚簇索引记录进行修改的时候的事务id。
roll_pointer:每次对哪条聚簇索引记录有修改的时候,都会把老版本写入undo日志中。这个roll_pointer就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(注意插入操作的undo日志没有这个属性,因为它没有老版本)

开始事务时创建readview,readview维护当前活动的事务id,即未提交的事务id,排序生成一个数组。
访问数据,获取数据中的事务id(获取的是事务id最大的记录),对比readview:
如果在readview的左边(比readview都小),可以访问(在左边意味着该事务已经提交)
如果在readview的右边(比readview都大)或者就在readview中,不可以访问,获取roll_pointer,取上一版本重新对比(在右边意味着,该事务在readview生成之后出现,在readview中意味着该事务还未提交)

已提交读隔离级别下的事务在每次查询的开始都会生成一个独立的ReadView,而可重复读隔离级别则在第一次读的时候生成一个ReadView,之后的读都复用之前的ReadView。
这就是Mysql的MVCC,通过版本链,实现多版本,可并发读写,写读。通过ReadView生成策略的不同实现不同的隔离级别。

有哪些情况会导致索引失效?

这里回答基于最新MySQL8 版本,MySQL8失效的以前版本也失效,MySQL8不失效的,以前可能会失效。
·使用 like 并且是左边带%,右边可以带会走索引(但是并不绝对,详细解释看下面like专题分析)	
.隐式类型转换,索引字段与条件或关联字段的类型不一致。(比如你的字段是int,你用字符串方式去查询会导致索引失效)。
·在where条件里面对索引列使用运算或者使用函数。
·使用 OR 且存在非索引列
·在 where条件中两列做比较会导致索引失效
.使用IN可能不会走索引(MySQL环境变量eq_range_index_dive_limit的值对IN语法有很大影响,该参数表示使用索引情况下IN中参数的最大数量。MySQL5.7.3以及之前的版本中,
eq_range_index_dive_limit的默认值为10,之后的版本默认值为200。我们拿MySQL8.0.19举例,eq_range_index_dive_limit=200表示当IN (..)中的值>200 个时,该查询一定不会走索引。<=200则可能用到索引。)
.使用非主键范围条件查询时,部分情况索引失效。
·使用order by 可能会导致索引失效
·is null is not null可能会导致索引失效

MySQL 的大表查询为什么不会爆内存

由于 MySQL 是边读边发,因此对于数据量很大的查询结果来说,不会再 server 端保存完整的结果集,所以,如果客户端读结果不及时,会堵住 MySQL 的查询过程,但是不会把内存打爆。
InnoDB 引擎内部,由于有淘汰策略,InnoDB 管理 Buffer_Pool 使用的是改进的 LRU 算法,使用链表实现,实现上,按照 5:3 的比例把整个 LRU 链表分成了 young 区域和 old 区域。对冷数据的全扫描,影响也能做到可控制。

B+ Tree

非叶子节点不存储data,只存储索引(冗余),可以放更多的索引(有序)
叶子节点包含所有索引字段
叶子节点用指针连接,提高区间访问的性能

首先分析B+树和B树的区别:
1.B+树上除叶子节点其他节点是不存储数据的。B树上每个节点都存储了key和data。而B+树只在叶子节点上存储数据,其他节点只存储key。
为什么要进行这样的改变呢?
以innoDB存储引擎来说,数据库中页的大小是固定的,每页的默认大小为16K。如果页不存储数据(data)就可以存储更多的key,那么就可以尽可能的减少树的高度。总结来说B+树相对于B树来说又矮又胖。这时候如果查询那么就会降低很多IO操作。
假定一个页16K可以存储1000个key。那么3层的B+树就可以存储100010001000=10亿条数据。而且根节点是常驻内存的,也就是说查询10亿条数据只需要进行2次IO操作。
2.B+树所有的数据都存储在叶子节点上并且是按照顺序排列的。那么使用B+树完成范围查找,排序查找,分组查找,去重查找就会很简单,效率也比较高。而B树就不可以。因为B树的数据是分散个各个节点上的。
3.B+树上每页之间是一个双向链表进行链接,叶子节点中的数据都是使用单向链表链接的。但是需要记得一点的是B树也可以加链表。
PS:InnoDB存储引擎中索引就是这样存储的。InnoDB中的聚集索引就是上面的结构。需要记住一点是MyISAM中依然使用的是B+树,结构是一样的,原理也是一样的,区别在于存储不是数据而是数据的文件地址。

InnoDB索引实现(聚集)

表数据文件本身就是按B+Tree组织的一个索引结构文件聚集索引
叶节点包含了完整的数据记录(主键索引)
对于非主键索引的索引结构,叶子结点包含了索引和主键。之后再去主键索引结构中查询获取数据。为了一致性和节省存储空间。

MyISAM索引实现(非聚集)

MyISAM索引文件和数据文件是分离的(非聚集) 叶子结点存储的是索引和数据的磁盘地址,而不是完整数据

联合索引

从最左边的字段开始排序,找到叶子结点的主键后,去对应的主键索引中查询。

BufferPool 缓存机制 

当数据更新时,先会更新缓存池里的数据,然后将更新记录写入Redo 日志里。之后通过IO线程,随机更新磁盘的数据。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值