MySQL之事务与锁机制

什么是数据库的事务?

按照我们思考问题的逻辑,一般从(4W+1H)What、How、Where、When、Why这几方面考虑。

  • 事务的本质是什么?
    事务时DBMS执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
    一般包含多个DML语句

数据库事务的典型使用场景是什么?有哪些存储引擎支持事务?事务的四大特性?以及什么时候出现事务?

典型使用场景: 多张表或者多行数据同时操作,或者说涉及到数据的增删改时一般要开启事务。查询操作不需要开启事务。

哪些存储引擎支持事务: InnoDB支持事务,MyISAM不支持。MyISAM属于MySQL早期的存储引擎,正式因为需求的驱动促进了InnoDB这个支持事务的存储引擎的出现,做到了保证数据的一致性。

事务的四大特性ACID及其实现机制

  1. 原子性 Atomicity
    事务是执行的最小单位。数据库的事务执行要么成功要么失败,不可以部分成功部分失败。
    在这里插入图片描述
  2. 一致性 Consistent
    原子性,隔离性和持久性最终都是为了保证数据的一致性,所以从这个角度来说一致性是终极目标。
    再者一致性也是为了保证数据的完整性约束。一致性也可分为数据库本身的一致性和用户定义的业务需求场景的一致性。
    用户定义的业务需求场景就比如银行转账业务的场景,在这里插入图片描述
  3. 隔离性 Isolation
    当有多个客户端连接到MySQL服务器上时,会有一系列的事务同时操作表中数据。这时候我们希望不同的事务之间对数据的操作互不影响,大家的操作互相都是透明的。简单来说就是不应该出现一个事务在修改某些数据的时候,另一事务读取到了不合法的数据。这就是由事务的隔离性来保证的。
  1. 持久性 Durable
    只要我commit了一个事务,那么对于数据库的修改一定是永久性的。不能因为其他的如系统重启或者系统宕机了而使得数据又恢复到了之前的状态。
    是依靠redo log来实现的

什么时候出现事务: 可以使用show variables like ‘autocommit%’来查看是否开启了自动提交事务。如果是未开启状态的话,需要手动提交事务。在MySQL中可以使用begin 或者 start transaction来开启事务,用rollback 或者 commit来结束事务

事务隔离性与锁的关系

事务并发带来的问题以及解决方案

有的朋友对事务并发的三大问题他们的区别仍然是含糊不清的,以及 事务并发出现的问题与事务隔离级别之间的区别也存在混淆。

事务并发的三大问题

场景一 脏读 在这里插入图片描述>场景二 不可重复读
在一个事务中前后两次同样的查询操作最终读取到的数据不一样是因为后一次读取到了另一事务已提交的数据。在这里插入图片描述>场景三 幻读
在这里插入图片描述

现在来谈谈三者的共同点和区别:

区别:首先脏读和不可重复读的区别是不可重复读是读取了已提交事务所修改的数据,脏读是读取了未提交事务。而不可重复读和幻读的区别是,不可重复读所针对的是删除和修改操作,而幻读针对的是插入操作。
共性:都是数据库的读一致性的问题,这样的问题必须由数据库本身提供一定的事务隔离机制来解决。(站在用户的角度来考虑,如果我还需要通过自己编写代码来处理读一致性的问题的话,那这个数据库就没有使用的必要了)

事务的隔离级别

事务的隔离级别是由数据库专家和数据库生产厂商联合制定的一套标准。SQL92[http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt]
可用全文搜索找到_isolation
在这里插入图片描述
未提交读 :允许一个事务读取另一个未提交事务所修改的数据。所以叫未提交读
已提交读:允许一个事务读取另一个已提交事务所修改的数据。所以称为已提交读
可重复读:保证在同一事务中执行多次相同的查询读取到的结果是一样的
串行化:最强的事务隔离级别,直接不让事务并行执行了,只能串行去执行。但是这样做的弊端是很大的,极大的降低了数据库的吞吐量。
在这里插入图片描述

事务隔离的实现原理

如果要解决读一致性的问题,保证一个事务中前后两次读取数据结果是一直的,那么要实现事务隔离,应该怎么去做?
思路:第一种:加锁,在读取数据之前就对其进行加锁,阻止其他事务对数据进行操作 这样的方式也称为LBCC(Lock Based Concurrency Control)。加锁可以解决问题但是当需要枷锁的操作都加上锁之后也会降低事务的并发度。所以有了第二种思路。
第二种:
既然要做到在一个事务中前后两次读取的数据保持一致,又不影响到其他事务对这个数据的操作的话,我们可以这样来做,在第一次读取这行数据的时候给它建立一个快照,当我下次再需要读取的时候,我直接去读快照中的数据。这样的方式也叫MVCC(Multi Version Concurrency Control)多版本并发控制

下面将谈到LBCC是如何实现的?
当前就直接读到数据,而不是通过快照来读,就需要用到锁了,那么在什么样的情况下或者说什么样的select/update/insert才会用到锁呢?
首先要了解到锁的分类,由三个基本的Lock mode分别是共享锁(S锁,粒度:行),排它锁(X锁,粒度:行),意向锁(粒度:表)。意向锁一般配合共享锁,排它锁一起使用。可以参考MySQL官网上的解释InnoDB锁机制

在这里插入图片描述
这里意向锁并不是锁住了一张表中的数据,而是起到一个类似于标记一样的作用,来提高我们加表锁的效率。为什么这么说呢?
在这里插入图片描述锁的作用是什么?
解决并发时的资源竞争问题。在多线程的程序中锁锁住的时一个对象或者说一块资源。
那在数据库中锁到底锁住了什么呢?

有两种假设,因为加锁的操作在SQL语句中是需要你指定某一个字段的。
所以假设一,锁住的是一行数据,假设二,锁住的是一个字段的数据。
在验证过程中发现,锁住一个字段是肯定错误的,而锁住一行数据是不全面的。因为锁真正锁住的是索引。那么锁住的是索引,就会存在以下几种情况
1.不使用索引的那个字段
2.使用建立主键索引的那个字段
使用主键索引的列,会用主键索引作为聚集索引来决定数据的存放方式
3.使用建立辅助索引的那个字段
当对使用辅助索引的字段加锁时,因为采用辅助索引查询,会有回表的情况发生,辅助索引所建立的B+树中存储了主键索引的键值和辅助索引,这样就会直接导致,对使用了辅助索引的字段加锁之后,再对该行数据中使用主键索引的字段加锁不成功了。反之也一样,操作主键索引,也会锁住辅助索引。

session 1
  select * from t1 where id =3 for update;      -- 该表中id是primary key,name是unique key
session 2
 select * from t1 where name='chen' for update;

推荐使用锁的方式
1.where = 字段 要尽可能精准,最好不要有表示范围的表达式(尽量别写between and 以及>= 、<=)
2.where = 字段,该字段最好是建立了索引的字段,如果是没有使用索引的字段,将造成一种锁表的现象,导致其他事务无法操作别的行。

事务隔离级别的实现方式

事务的隔离级别是依靠锁和快照来实现的。
Repeatable Read
普通的select是采用快照读的方式
而加锁的select和更新是依赖锁来实现的,其中间隙锁是可重复读这个隔离级别中独有的技术
在这里插入图片描述Read Uncommited
不加锁,可以任意读取数据
Serializable
所有的select语句都会被隐式转化为select……lock in share mode
会和update、delete语句产生互斥。

这里还有一个值得思考的问题,
在这里插入图片描述这张图片是出现在本文中的第二次了,对于普通select语句来说,对于RC隔离级别并未解决不可重复读的问题,而在采用了同样的MVCC技术的RR隔离级别中却解决了这个问题。这是因为他们操作的时间节点不同所导致的。就是说事务的可见性是不同的。
在RR级别下,在我这一个事务开启之后,只能查询到我执行第一次查询之前的数据
而RC级别下,则是能查询到当前最近一次查询之前的数据。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值