11 事务与锁

1 事务的基础知识

​ 让数据库始终保持一致性,也可以通过事务回滚到上一个commit点;

​ 事务就是对ACID的一个实现;

INNODB支持事务,myISAM不支持事务

1.1 事务的ACID
1.原子性(A)
要么全部成功,要么失败回滚,没有中间状态;

2.一致性(C)
事务执行前后,数据从一个合法性状态变化为另一个合法性状态,不能违背字段本身的逻辑约束;

3.隔离性(I)
一个事务的执行不能被其它的事务干扰,即一个事物内部的操作及使用的数据对并发的其它事务是隔离的,并发执行的各个事务之间互不干扰;

4.持久性(D)
事务提交后对数据的变更是持久存储的,持久性是通过事务日志来保障,日志包括'REDO日志'与'UNDO日志'
1.2 使用事务
1.事务的完整过程
-- 开启事务
BEGIN;
-- DML操作
INSERT INTO student_info(id,student_id,NAME,course_id) VALUES(2,12,'lily',7509);	#此时不会自动提交数据
INSERT INTO student_info(id,student_id,NAME,course_id) VALUES(2,12,'lily',7509); #受主键影响不能添加成功
-- 提交事务
-- 结束的状态:事务提交的状态(COMMIT)、中止的状态(ROLLBACK)
COMMIT;

-- 默认情况下,DML操作是自动 COMMIT
2.隐式事务 
-- SET autocommit = FALSE
-- 默认情况下DML操作都是一个独立的事务,默认autocommit = ON
-- DDL操作会自动 COMMIT
1.3 数据并发的问题
脏写:事务A修改了事务B未commit的数据,事务Acommit之后,事务B又rollback,结果出现脏写;
脏读:事务A读取了事务B更新后但未commit的数据,事务B又rollback了,事务A出现了脏读;
不可重复读:事务A在事务开启期间每次读取的一行数据,由于该数据被其它事务修改了,造成了每次读的不一样;
幻读:事务A在读取数据时,由于其它事务已经新增或者删除行时,造成读取的数据量不一样,称为幻读;
1.4 MySQL中的四种隔离级别
1.读未提交(Read Uncommitted)
    最低的隔离级别,也是最不安全的隔离级别。在这个级别下,一个事务可以读取另一个事务未提交的数据,这可能会导致脏读、不可重复读和幻读等问题。

2.读已提交(Read Committed)
    在“读已提交”隔离级别下,每个事务只能看到已经提交的数据,而不能看到其他事务未提交的数据。这意味着,如果一个事务正在修改某个数据,另一个事务在此时读取该数据,那么它读取到的是修改前的数据,而不是修改后的数据。
    需要注意的是,“读已提交”隔离级别下,由于每个事务都需要获取读锁,因此可能会出现锁竞争的情况,导致性能下降。此外,由于每个事务只能看到已经提交的数据,因此可能会出现“脏读”、“不可重复读”和“幻读”等问题。

3.可重复读(Repeatable Read)--MySQL默认
    它可以保证在同一个事务中多次读取同一数据时,读取到的数据是一致的,即使在事务执行期间有其他事务对该数据进行了修改。
    可重复读隔离级别的实现方式是在事务开始时,对所有读取的数据进行加锁,直到事务结束才释放锁。这样可以保证在事务执行期间,其他事务无法修改被锁定的数据,从而保证了数据的一致性。

4.串行化(Serializable)
    每个事务都必须等待前一个事务完成后才能开始执行。这样可以避免并发事务之间的任何冲突,保证了数据的完全一致性。但是,这种级别下的性能开销非常大,因为每个事务都必须等待前一个事务完成后才能开始执行,这会导致大量的等待和阻塞。
1.查看当前session的默认事务隔离级别
-- SHOW VARIABLES LIKE 'transaction_isolation'

2.设置事务的隔离级别
-- SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL '隔离等级'
隔离等级:
-- READ UNCOMMITTED
-- READ COMMITTED
-- REPEATABLE READ
-- SERIALIZABLE
2 表级锁
X锁:排它锁
S锁:共享锁

X锁:事务对一条记录进行写操作的时候,获取了X锁,则其它事务就不能同时获取S锁或者X锁,也就是说,其他事务不能同时读取或修改被锁定的数据。只有当前持有X锁的事务可以对被锁定的数据进行修改操作。X锁的作用是保证数据的一致性和完整性,防止多个事务同时修改同一数据造成数据混乱或丢失。

S锁:当一个事务获取了一个表或行的S锁时,其他事务也可以获取该表或行的S锁,但是不能获取该表或行的X锁(排他锁)。S锁的作用是允许多个事务同时读取同一份数据,但是不允许任何事务对该数据进行修改。
1. 多个事务可以同时获取同一份数据的S锁;
2. S锁不会阻塞其他事务的读取操作;
3. S锁会阻塞其他事务的写入操作;
4. S锁不会阻塞其他事务的S锁获取操作
MySQL意向锁
IX与IS
用于在多个事务同时访问同一数据时,协调它们之间的锁定行为。意向锁分为两种类型:意向共享锁(IS)和意向排他锁(IX)。
当一个事务需要对某个数据行进行共享锁定时,它会先请求意向共享锁(IS)。如果当前没有其他事务持有排他锁(X)或意向排他锁(IX),则该事务可以获得共享锁。如果有其他事务持有排他锁或意向排他锁,则该事务必须等待,直到这些锁被释放。
同样地,当一个事务需要对某个数据行进行排他锁定时,它会先请求意向排他锁(IX)。如果当前没有其他事务持有排他锁(X)或意向锁(IS或IX),则该事务可以获得排他锁。如果有其他事务持有共享锁或意向共享锁,则该事务必须等待,直到这些锁被释放。
意向锁的作用是协调事务之间的锁定行为,避免出现死锁等问题。
MySQL自增锁
保证自增列在插入数据时自增ID的唯一性;
MySQL的自增锁机制就是为了解决这个问题而设计的。当一个事务需要插入一条记录时,它会先获取自增锁,然后再获取自增ID。在获取自增ID的过程中,其他事务是无法获取自增锁的,因此可以保证每个事务获取到的自增ID是唯一的。
MySQL元数据锁
用于保护数据库中的元数据(例如表结构、索引、触发器等)不被并发修改或删除。当一个事务需要修改或删除元数据时,它必须先获取一个元数据锁,以防止其他事务同时修改或删除同一元数据。元数据锁是一种特殊的锁,它不同于普通的行级锁或表级锁,它只在需要修改元数据时才会被获取。元数据锁的存在可以保证数据库的一致性和安全性。
3 行级锁
记录锁
在InnoDB中,记录锁是一种行级锁,它是用来保护单个行的锁定,以防止其他事务对该行进行修改或删除。
记录锁是在事务中使用的,当一个事务需要修改或删除某个行时,它会在该行上设置一个记录锁,以防止其他事务同时修改或删除该行。在事务提交或回滚后,记录锁会被释放。
记录锁的优点是可以提高并发性能,因为它只锁定需要修改或删除的行,而不是整个表。但是,如果事务需要锁定大量的行,那么记录锁可能会导致性能下降,因为其他事务需要等待锁释放才能继续执行。
间隙锁
在InnoDB中,间隙锁是一种特殊的锁类型,它锁定了一个范围内的键值之间的间隙,而不是锁定实际的行。
当一个事务在InnoDB中执行一个范围查询时,它会锁定查询范围内的所有行,同时也会锁定范围之间的间隙。这是为了防止其他事务在这个范围内插入新的行,从而导致数据不一致。
间隙锁的作用是保证数据的一致性和完整性,但是它也会影响并发性能。因为间隙锁会锁定范围之间的间隙,所以其他事务无法在这个范围内插入新的行,从而导致并发性能下降。
因此,在使用InnoDB存储引擎时,需要注意间隙锁的使用,避免不必要的锁定,从而提高并发性能。
临键锁:
临键锁是指在执行某些操作时,InnoDB会自动对涉及到的索引键进行锁定,以保证数据的一致性和并发性。
举个例子,假设有一个表t,其中有一个索引键a,现在有两个事务T1和T2,它们都要对t表中的某些行进行更新操作。如果T1先对某些行进行了更新,并且对a索引键进行了锁定,那么T2在更新这些行时,就会被阻塞,直到T1释放了对a索引键的锁定。
临键锁的优点是可以减少死锁的发生,因为它只会锁定涉及到的索引键,而不是整个表或整个数据页。但是,如果应用程序的并发性很高,临键锁可能会导致锁争用的问题,从而影响系统的性能。因此,在使用临键锁时,需要根据具体情况进行权衡和优化。
插入意向锁:
InnoDB的插入意向锁是一种锁定机制,用于在多个事务同时插入数据时,保证数据的一致性和完整性。当一个事务要向表中插入数据时,它会先获取一个插入意向锁,表示它有意向向表中插入数据。其他事务也可以获取插入意向锁,但是它们必须等待当前持有插入意向锁的事务完成插入操作后才能进行插入操作。

插入意向锁是一种轻量级锁,它不会阻塞其他事务的读操作,只会阻塞其他事务的写操作。这样可以保证在多个事务同时插入数据时,不会出现数据丢失或者数据冲突的情况。

需要注意的是,插入意向锁只是一种意向锁,它并不会真正锁定表或者行,只是告诉其他事务当前事务有意向进行插入操作。如果当前事务没有真正进行插入操作,那么插入意向锁也会被释放。
4 乐观锁
MySQL的乐观锁是一种基于版本号的锁机制,它通过在数据表中添加一个版本号字段,来实现对数据的并发控制。当多个事务同时对同一行数据进行修改时,乐观锁会先检查该行数据的版本号是否与当前事务的版本号一致,如果一致,则允许该事务进行修改操作,否则会回滚该事务。

乐观锁的实现方式通常有两种:

1. 版本号机制:在数据表中添加一个版本号字段,每次更新数据时,将版本号加1,当多个事务同时对同一行数据进行修改时,只有版本号相同的事务才能进行修改操作。

2. 时间戳机制:在数据表中添加一个时间戳字段,每次更新数据时,将时间戳更新为当前时间,当多个事务同时对同一行数据进行修改时,只有时间戳最新的事务才能进行修改操作。

乐观锁的优点是不会阻塞其他事务的读操作,因为它不会对数据进行加锁,只是在更新数据时进行版本号的比较。但是,乐观锁也有一些缺点,比如在高并发的情况下,由于多个事务同时对同一行数据进行修改,会导致大量的回滚操作,降低系统的性能。
5 悲观锁
MySQL的悲观锁是一种保守的锁机制,它假设在任何时候都会有其他事务试图访问同一行数据,因此在访问数据之前就会加上锁,以防止其他事务对该数据进行修改。

悲观锁的实现方式是在事务中使用SELECT ... FOR UPDATE语句,它会在读取数据时对数据行加锁,直到事务结束才会释放锁。这种锁机制可以保证数据的一致性和完整性,但是会降低并发性能,因为其他事务需要等待锁的释放才能访问数据。

悲观锁的优点是可以避免数据的并发修改,保证数据的正确性和完整性,适用于对数据一致性要求较高的场景。但是它的缺点是会降低并发性能,因为其他事务需要等待锁的释放才能访问数据,同时也容易引起死锁问题。
6 死锁及解决
MySQL死锁是指两个或多个事务在相互等待对方释放锁资源的情况下,陷入了无限等待的状态,导致数据库无法继续执行。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值