FreezeJ' Blog

Mysql事务

2022-08-16

https://blog.csdn.net/qq_42490630/article/details/107200249
https://www.cnblogs.com/cqqfboy/p/15179597.html

Mysql事务

事务保证多个任务执行是否同时成功或者同时失败。
事务支持是在引擎层实现的,Mysql的默认引擎InnoDB是支持事务的,MyISAM不支持事务。

事务的4个特性(ACID)

原子性(atomicity)

一个事务的一系列操作相当于一个整体(不可分割),要么全部成功,要么全部失败。

一致性(consistency)

事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。

隔离性(isolation)

事务的隔离性是指在并发环境中,并发的事务时相互隔离的,一个事务的执行不能不被其他事务干扰。

持久性(durability)

一旦事务提交,那么它对数据库中的对应数据的状态的变更就会永久保存到数据库中。

隔离机制

SQL标准中规定了4种事务隔离级别,就是说多个事务并发运行的时候,互相是如何隔离的,从而避免一些事务并发问题。

脏读

两个并发的事务,事务A读取了事务B中修改但是尚未提交的数据。一旦事务B回滚,这个数据就是无效数据。

不可重复读

指一个事务范围内,多次查询某个数据,却得到不同的结果。(多次查询事务过程中,数据被另一个事务修改并提交了)

幻读

用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。

不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。Mysql默认的隔离级别是可重复读(RR)

查看隔离级别:

mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+

读未提交(read uncommitted)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。

读已提交(read committed)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。

可重复读(repeatable read)

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。

串行化(serializable)

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

不同隔离级别解决的问题

锁机制

http://t.zoukankan.com/hgmyz-p-12351104.html
https://blog.csdn.net/weixin_41635750/article/details/118347133

表级锁

表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。
最常使用的MYISAM与INNODB都支持表级锁定。

特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。

页级锁

页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁.表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录

特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

行级锁

行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。

特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

数据库的增删改操作默认都会加排他锁,而查询不会加任何锁。

共享锁

对某一资源加共享锁,自身可以读该资源,其他人也可以读该资源(也可以再继续加共享锁,即 共享锁可多个共存),但无法修改。要想修改就必须等所有共享锁都释放完之后。

select * from table lock in share mode;

排他锁

对某一资源加排他锁,自身可以进行增删改查,其他人无法进行任何操作。
增删改操作,默认就会上排他锁,不需要显式说明。

select * from table for update;

意向锁

Innodb特有,分为意向共享锁和意向排他锁

  • 意向共享锁: 表示事务获取共享锁时,必须先得获取该表的意向共享锁
  • 意向排他锁: 表示事务获取排他锁时,必须先得获取该表的意向排他锁

如果要对整个表加锁,必须保证表内不存在任何锁。如果一行行的去检查是否加锁,效率必然极低,这时候可以检测意向锁是否被占用即可。

死锁

当多个事务同时持有和请求同一资源上的锁而产生循环依赖的时候就产生了死锁,解决方法为:

  1. 用更低的隔离级别;
  2. 以固定的顺序访问你的表和行;
  3. 添加精心选定的索引到表中;
  4. 使用更少的锁定。

事务超时时间:默认参数innodb_lock_wait_timeout设置锁等待的时间是50s

# 查询全局等待事务锁超时时间
SHOW GLOBAL VARIABLES LIKE 'innodb_lock_wait_timeout';

乐观锁和悲观锁都是一种思想,并不是真实存在于数据库中的一种机制。

乐观锁

总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。

乐观锁优点是效率高,缺点是更新失败的概率比较高。

使用场景:如果只要保证数据最终一致性,可以用乐观锁。

悲观锁

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,在做操作之前先上锁。比如行锁,表锁等,读锁,写锁等,这种都属于悲观锁。

悲观锁依赖于数据库锁机制,更新失败的概率比较低,但是效率也低。

使用场景:在流量不大,对数据一致性要求又非常高的时候,可以用悲观锁。

事务操作命令

# 开启一个事务
begin;  # 或start transaction;

# SQL操作
insert into projects values(15, '测试', 'test');

# 提交操作
commit;
# 开启一个事务
begin;  # 或start transaction;

# SQL操作
insert into projects values(16, '测试1', 'test1');

# 设置保存点A
SAVEPOINT A;

# SQL操作
insert into projects values(17, '测试2', 'test2');

# 查询插入结果
select * from projects;

# 回滚操作
rollback to A;

# 提交
commit;
Tags: Mysql