2021-2-25数据库事务,脏读,不可重复读,幻读现象,行级锁,表级锁,共享锁,排他锁详解

什么是事务(transaction)?
事务,就是要做的或所做的事情,数据库事务指的则是作为单个逻辑工作单元执行的一系列操作(SQL语句)。这些操作要么全部执行,要么全部不执行。一个事务可以包含多条sql语句

事务的四大特性:acid特性
1、原子性(atomicity)
事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行(不可分割)
2、一致性(Consistency)
事务应确保数据库的状态从一个一致状态转变为另一个一致状态,例如转账行为中,一个人减了50元,另外一个人就应该加上这50元,而不能是40元。其他一致状态的含义是数据库中的数据应满足完整性约束,
例如字段约束不能为负数,事务执行完毕后的该字段也同样不是负数(两者需要保持一致)
3、隔离性(Isolation)
多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
4、持久性(Durability)
一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。

为何要用事务
1、为数据库操作提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
2、当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。

事务的三种开启方式
自动提交事务(自动(隐式)开启,自动(隐式)提交)
每一条单独的SQL语句都在其执行完成后进行自动提交事务,即执行 SQL 语句后就会马上自动隐式执行COMMIT(提交) 操作。如果出现错误,则进行事务回滚至之前状态

显式事务(显式开启,显示提交)
通过指定事务开始语句来显式开启事务来作为开始,并由以提交命令或者回滚命令来提交或者回滚事务作为结束的一段代码就是一个用户定义的显式事务

隐式事务(隐式开启,显示提交)
在隐式事务中,无需使用BEGIN或START TRANSACTION 来开启事务,每个SQL语句第一次执行就会开启一个事务,直到用COMMIT[TRANSACTION](可以省略)来提交或者ROLLBACK [TRANSACTION]
(可以省略)来回滚结束事务。

总结:MYSQL 默认为每条sql开启事务,并且会在本条sql执行完毕后自动执行commit提交,若想设置手动提交有两种方式
方式一:直接用 SET 来改变 MySQL 的自动提交模式(下述设置均为会话级别的设置):
SET AUTOCOMMIT=0 禁止自动提交
SET AUTOCOMMIT=1 开启自动提交

方式二: 手动开启的事务里默认不会自动提交,所以我们可以将要执行的sql语句放在我们自己手动开启的事务里这种方式在当你使用commit或者rollback后,事务就结束了,再次进入事务状态需要再次start transaction

开启事务:start transaction;或者begin;
commit;提交事务
rollback;回滚事务

并发操作数据带来的问题:
以下的三种情况,在数据库管理机制的干扰下,很可能不存在,但我们还是要了解,防止出现下列问题是不知道如何处理

脏读
读到无效的数据,就是事务一更新了一个数据(未提交),此时事务二查询了这个数据(读到的是未提交的数据),同时事务一将数据回滚(rollback),此时事务二读到的就是脏数据(不真实的数据)

不可重复读
事务T1读取一行记录,紧接着事务T2修改了T1刚才读取的那一行记录并且提交了。然后T1又再次读取这行记录,发现与刚才读取的结果不同。这就称为“不可重复”读,因为T1原来读取的那行记录已经发生了变化。

幻读
事务T1读取或修改了指定的WHERE子句所返回的结果集。然后事务T2新插入一行记录,这行记录恰好可以满足T1所使用的查询条件中的WHERE 子句的条件。然后T1又使用相同的查询再次对表进行检索,但是此时却看到了事务T2刚才插入的新行或者发现了处于WHERE子句范围内,但却未曾修改过的记录。像是出现幻觉了一样

解决方案:
所以这些问题的本质,都是数据库的多事务并发问题,那么为了解决多事务并发带来的脏读、不可重复读、幻读等读等问题,数据库才设计了锁机制、事务隔离机制、MVCC 多版本隔离机制,用一整套机制来解决多事务并发问题

数据库锁机制:
什么是锁?为何要加入锁机制?
锁是计算机协调多个进程或线程并发访问某一资源的机制,因为在数据库中,除了传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供需要用户共享的资源。锁机制可以保护数据库中数据的安全,保证数据库中数据的一致性与有效性

mysql中锁的分类:
一、按锁的粒度划分,可分为行级锁、表级锁、页级锁。(mysql支持)
行级锁
是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁 和 排他锁。
特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
支持引擎:InnoDB
行级锁定分为行共享读锁(共享锁)与行独占写锁(排他锁)
用法:共享锁(S):SELECT * FROM table_name WHERE … LOCK IN SHARE MODE
排他锁(X):SELECT * FROM table_name WHERE … FOR UPDATE

表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。
特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。
支持引擎:MyISAM、MEMORY、InNoDB
分类:表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)
用法:lock table 表名 read(write),表名 read(write),…;

页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。BDB支持页级锁
特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

二、按锁级别划分,可分为共享锁、排他锁
排他锁
1、对于insert、update、delete语句,InnoDB会自动给涉及的数据加锁,而且是排他锁(X)也就是说针对数据写的业务,数据库都是都默认加上排他锁;
加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过forupdate和lock in sharemode锁的方式查询数据,但可以直接通过select…
from …查询数据,因为普通select查询没有任何锁机制。如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁或不加锁(在其他事务里一定不能再加排他锁,但是在事务T自己里面是可以加的),反之亦然。

共享锁就是多个事务对于同一数据可以共享一把锁,获准共享锁的事务只能读数据,不能修改数据直到已释放所有共享锁,所以共享锁可以支持并发读
2、对于普通的select语句,InnoDB不会加任何锁,需要我们手动自己加,可以加两种类型的锁,用法如上所示

三、按使用方式划分,可分为乐观锁、悲观锁

四、按加锁方式划分,可分为自动锁、显式锁

五、按操作划分,可分为DML锁、DDL锁

DML锁(data locks,数据锁),用于保护数据的完整性,其中包括行级锁(Row Locks (TX锁))、表级锁(table lock(TM锁))。

DDL锁(dictionary locks,数据字典锁),用于保护数据库对象的结构,如表、索引等的结构定义。其中包排他DDL锁(Exclusive DDL lock)、共享DDL锁(Share DDL lock)、可中断解析锁(Breakable parse locks)

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/givenchy_yzl/article/details/114087938