MySQL数据库事务与锁的原理和用法

这篇文章主要介绍“MySQL数据库事务与锁的原理和用法”,在日常操作中,相信很多人在MySQL数据库事务与锁的原理和用法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”MySQL数据库事务与锁的原理和用法”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

MySQL事务

    事务的特性ACID,分别表示原子性(atomicity)、一致性(consistency)、隔离性(isolation)、和持久性(durability)。一个运行良好的事务处理系统,必须具备这些标准特性。

    1. 原子性(atomicity)
    一个事务必须被视为一个不可分割的最小工作单元,整个事务中所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来讲,不可能只执行其中的一部分操作,这就是事务的原子性。

    2. 一致性(consistency)
    数据库总是从一个一致性的状态转换到另一个一致性的状态。

    3. 隔离性(isolation)
    通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。

    4. 持久性(durability)
     一旦事务提交,则其所做的修改就会永久的保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。

隔离级别

    隔离性其实比想象中的要复杂。在SQL标准中定义了四种隔离级别,每一种隔离级别都规定了一个事务中所做的修改,哪些在事务内或事务间是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统开销也低。

READ UNCOMMITTED(未提交读)
    在 READ UNCOMMITTED 级别,事务中修改,即使没提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读。这个级别会导致很多问题,从性能上来说 READ UNCOMMITTED 不会比其他的级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。

READ COMMITTED(提交读)
    大多数数据库系统的默认隔离级别都是 READ COMMITTED (但MySQL不是)。READ COMMITTED满足前面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读(nonrepeatable read),因为两次执行同样的查询,可能会得到不一样的结果。

REPEATABLEB READ(可重复度)
    REPEATABLEB READ解决了脏读的问题。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读(Phantom Read)的问题。所谓幻读,指的是当某个事务在读群某个范围内的纪录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的纪录时,会产生幻行(Phantom Row)。InnoDB和XtraDB存储引擎通过多版本并发控制器(MVCC,Multiversion Concurrency Control)解决了幻读的问题。可重复读是MySQL的默认事务隔离级别。

SERIALIZABLE(可串行化)
    SERIALIZABLE 是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。加单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用问题。实际应用中也很少会用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。

InnoDB采用MVCC来支持高并发,并实现了四个隔离级别。其默认级别是 REPEATABLEB READ(可重复度),并且通过间隙锁(next-key locking)策略防止幻读的出现。间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影行的插入。

ANSI SQL隔离级别

隔离级别 脏读可能性 不可重复度可能性 幻读可能性 加锁读
READ UNCOMMITTED YES YES YES NO
READ COMMITTED NO YES YES NO
REPEATABLEB READ NO NO YES NO
SERIALIZABLE NO NO NO YES

下面测试一下MySQL在 REPEATABLEB READ 隔离级别下能否避免幻读(前提是InnoDB引擎)
在customer表做测试
MySQL数据库事务与锁的原理和用法

在A事务开启事务,查询年龄在12~16的结果
MySQL数据库事务与锁的原理和用法
结果如下:此时A事务并没有提交
MySQL数据库事务与锁的原理和用法

B事务,在customer表中插入一条age = 14的数据
MySQL数据库事务与锁的原理和用法
select语句结果为
MySQL数据库事务与锁的原理和用法
此时B事务未提交,customer表依然是
MySQL数据库事务与锁的原理和用法
说明 REPEATABLEB READ 隔离级别可以避免脏读。B事务commit之后customer表为
MySQL数据库事务与锁的原理和用法
此时A事务再执行select语句结果依然为
MySQL数据库事务与锁的原理和用法
说明MySQL数据库在 REPEATABLEB READ 隔离级别下可以避免幻读(InnoDB引擎)。commit后再次执行select结果为
MySQL数据库事务与锁的原理和用法
现在就恢复了B事务的操作。

结论:MySQL数据库在 REPEATABLEB READ 隔离级别下可以避免幻读(InnoDB引擎)。数据库的事务是为了保证数据的安全,事务特性中的一致性相对重要一些。

锁是为了保证事务的隔离性。锁是基于索引实现的。

读锁和写锁
    共享锁(shared lock)和排他锁(exclusive lock)也叫读锁(read lock)和写锁(write lock)
读锁是共享的,互相不阻塞的,多个客户在同一时刻可以同时读取同一个资源,而互不干扰。读锁对写锁阻塞。写锁则是排他的,也就是说一个写锁会阻塞其他的读锁和写锁,这是出于安全策略的考虑,只有这样,才能确保在给定的时间里,只有一个用户能执行写入,并防止其他用户读取正在写入的统一资源。

    写锁比读锁有更高的优先级,一个写锁的请求可能会被插入到读写队列的前面。

行锁和表锁
    表锁是MySQL最基本的锁策略,并且是开销最小的策略。会锁定整张表。没有命中索引的操作会锁表
    行锁可以最大程度的支持并发处理,同时带来了最大的锁开销 InnoDB 和 XtraDb 引擎实现了行级锁。会锁住某一行或者某几行,表中其他行的数据可以进行读写操作。命中索引,则会将某一行或者某几行加锁。

死锁
    两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。InnoDB处理死锁的方式是,将持有最少行级排他锁的事物进行回滚。锁的行为和顺序是和存储引擎相关的。以同样的顺序执行语句,有些存储引擎会产生死锁,有些则不会。

悲观锁和乐观锁
    悲观锁,假设丢失更新一定存在;sql后面加上for update(排他锁)。
   
乐观锁,假设丢失更新不一定发生。update时候存在版本,更新时候按版本号进行更新。

记录锁、间隙锁和临键锁
    记录锁,会锁住某一行数据,条件命中主键索引。where id = ?;如果在主键上加范围查询(where id > ?),记录锁会退化成间隙锁,锁住不符合条件的最近的值开始的所有。
    间隙锁,会锁住一个范围,条件命中非主键、非唯一索引,只有在重复读的隔离级别下存在。如果是(where age > ?) ,锁住不符合条件的最近的值开始的所有(前闭后开),也就是锁住索引中的间隙。
    临键锁,InnoDB默认行级锁,条件命中非主键、非唯一索引,会锁住一个范围,查出来的范围以及相邻的下一个区间都锁住。where age = ?则该值两侧的值的范围内被锁住(前闭后开)。

意向共享锁和意向排他锁
    意向共享锁,当一个事务试图对整个表进行加共享锁之前,首先需要获得这个表的意向共享锁。
    意向排他锁,当一个事务试图对整个表进行加排它锁之前,首先需要获得这个表的意向排它锁。
    

到此,关于“MySQL数据库事务与锁的原理和用法”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!