本文将继续深入探讨 MySQL,这是关系型数据库中的核心技术,被广泛应用于数据存储和管理。
一、事务的定义
事务:指数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作,是一组不可再分割的操作集合,要么全部执行成功,要么全部执行失败
二、事务的特性
1、A:原子性,一个事务中的操作要么全部完成,要么全部不完成
2、C:一致性,一个事务执行之前和执行之后都必须处于一致性状态
3、I:隔离性,同一时间,只允许一个事务请求同一数据,事务间互不干扰
4、D:持久性,一个事务一旦被提交了,那么对数据库中数据的改变就是永久的
三、脏读、幻读、不可重复读
1、脏读:一个事务读取了另一个事务未提交的数据或已经回滚的数据
2、幻读:一个事务两次执行同一条sql语句,读取的数据量不一样,因为二次读之前其他事务删除或新增了数据
3、不可重复读:一个事务两次执行同一条sql语句,读取的数据不一致,因为二次读之前其他事务修改了数据
四、事务的隔离级别
1、读未提交:一个事务可以读另一个事务未提交的数据,隔离级别最低
2、读已提交:一个事务只能读取另一个事务已经提交的数据
3、可重复读:一个事务多次取多一个记录,两次读取的数据是一样的(即使有其他事务对该条记录进行了修改)
4、串行化:要求事务串行化执行,也就是只能一个接着一个的执行
五、mysql事务隔离是如何实现的
隔离的实现主要是读写锁和MVCC
1、读写锁—最简单直接的事务隔离实现方式
(1)每次读操作需要一个共享读锁,每次写操作都要获取一个写锁
(2)共享锁之间不会产生互斥,共享锁和写锁之间,以及写锁和写锁之间会产生互斥
(3)当产生锁竞争时,需要等待其中一个操作释放锁之后,另一个操作才能获取
(4)行锁(记录锁):解决多个事务同时更新一行数据
(5)间隙锁:解决多个事务同时更新多行数据
2、MVCC
MVCC的实现依赖于:隐藏字段、Read View、undo log
(1)隐藏字段
DB_TRX_ID:标识最近一次对本行记录作修改(insert、update)的事务的标识符:即最后一次修改本行记录的事务id,如果是delete操作,在innodb存储引擎内部也属于一次update操作,即更新行的一个特殊位,将行标识为已删除,并非真正删除
DB_ROLL_PTR:回滚指针,指向该行的undo log,如果该行未被更新,则为空
DB_ROW_ID:如果没有设置主键且该表没有唯一非空索引时,innodb会使用该id来生成聚簇索引
(2)Read View:记录和隔离不同事务并发时此记录的哪些版本是对当前访问事务可见的
(3)undo log:除了用来回滚数据,还可以读取可见版本的数据,以此实现非锁定读
六、mysql事务一致性,原子性是如何实现的
1、通过锁和mvcc实现了执行过程中的一致性和原子性
2、在灾备方面通过redo log实现,redo log会把事务在执行过程中多数据库所做的所有修改都记录下来,在之后系统崩溃重启后可以把事务所作的任何修改都恢复出来
七、mysql的持久性是如何实现的
通过redo log实现
八、表级锁和行级锁有什么区别
1、表级锁:串行化时,整表加锁,事务访问表数据时需要申请锁,虽然分为读锁和写锁,但毕竟是锁住整张表,会导致并发能力下降
2、行级锁:除了串行化时innodb使用的都是行级锁,只锁一行数据,其他行不受影响,并发能力强,行级锁是由MVCC完成的
九、select for update
这个语句会让select语句产生一个排他锁(在进行写操作之前申请并获得,其他事务不能再获得任何锁),这个锁和update的效果一样,会使两个事务无法更新同一条记录
十、mysql会产生的日志
1、错误日志
2、慢查询日志:用来记录在mysql中响应时间超过阈值的语句
3、一般查询日志:记录了客户端连接信息以及执行的sql语句信息
4、redo log(重写日志):记录事务执行后的状态
5、undo log(回滚日志):主要用来回滚到某一个版本,是一种逻辑日志
6、bin log(二进制日志):记录mysql中增删改时的记录日志,最大用处是主从复制以及数据库的恢复
十一、mysql死锁问题以及解决方案
1、mysql中的死锁现象
假设有如下表:
id(主键索引) | no(非主键索引) | name |
1 | 1001 | 小明 |
2 | 1002 | 小李 |
3 | 1003 | 小华 |
4 | 1004 | 小黄 |
下面有两个事务,分别为事务A,事务B
上述两个事务的执行流程如下:
(1)在上述两个事务中,事务A首先开启了,然后执行一条查询的sql语句。因为记录的最大值为1004,1007不在这个范围。此时,事务A对于表当中no字段范围为(1004,正无穷)的no索引加上了一把间隙锁
(2) 事物B开启了,因为no值为1008的记录,不在范围(1004,+∞)的范围之内。因此,事物B也会加一个间隙锁,范围是(1004,+∞);由于间隙锁之间是互容的,因此事物B在执行select语句的时候,不会发生阻塞。
(3) 事物A执行了一条插入的索引为1007的数值。但是,由于事物B对于事物A插入的范围加上了间隙锁,因此事物A一定要等待到事物B释放锁,才可以继续执行。
(4)事物B执行了一条插入的索引值为1008的sql语句。但是,由于事物A对于(1004,+∞)的范围加锁了。因此,事物B一定需要等待到事物A释放锁,才可以继续执行。
可以看到,此时,两个事物互相阻塞了。
2、如何避免mysql当中的死锁现象
(1)设置任务超时等待时间:当在一个任务的等待时间超过了这个时间之后就进行回滚
(2)主动开启死锁检测:当innodb检测发现死锁之后,就可以进行回滚死锁的事务
(3)对于频繁更新的字段,采用唯一索引的设置方案