4. 事务和锁

mysql 文章 2022-07-15 14:22 0 全屏看文

事务

  • 原子性

    事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。

  • 一致性:

    执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。

  • 隔离性

    一个事务的执行不能被其他事务干扰。即一个事物内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事物之间不能互相干扰

  • 持久性

    一个事务的执行不能被其他事务干扰。即一个事物内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事物之间不能互相干扰

begin;   #开始事务...rollback;  #回滚事务savepoint 回滚点;  #添加回滚点rollback to 回滚点; #回滚到指定回滚点...commit; #提交事务

并发三大问题

  • 丢失修改:两个都改

    事务T1和事务T2同时读入同一数据进行修改,T1或者T2修改数据丢失的问题

  • 不可重复读:一读一改

    事务T1读取数据后,事务T2修改数据,使得T1两次读取的值不一致的问题

  • 读脏数据的问题:一读一改 + 事务撤销

    事务T2修改数据,事务T1读数据,T2事务撤销,T1读取数据不正确

排他锁和共享锁

  • 排他锁:写锁

    当前事务给数据添加写锁后,其他事务既不能读,也不能写,也不能添加锁。

  • 共享锁:读锁

    当前事务给数据添加读锁后,其他事务仅可以读,当前数据所有读锁释放后,才可以添加写锁。

  • 一级封锁协议

    事务T在修改数据R之前必须获得排他锁(解决丢失修改问题)

  • 二级封锁协议

    一级封锁协议+事务T在读数据R之前必须先对数据添加共享锁,读完立即释放(解决丢失修改和读脏数据的问题)

    因为事务结束前释放了共享锁,因此其他事务可以进行修改数据,因此无法解决不可重复读问题。

  • 三级封锁协议

    一级封锁协议+事务T在读数据R之前必须先对数据添加共享锁,且事务结束后才释放共享锁。

事务隔离

  • 读-未提交(read uncommitted):未上锁

    事务T1可以查看事务T2未提交的数据。(读脏数据的问题)

  • 读-已提交(read committed): 上了写锁,提交后解锁

    事务T1只能读取到T2已经提交后的数据。(不可重复读)

  • 可重复读(repeatable read): 三级封锁协议

    事务T1读取一张表时,事务T2不能修改那张表的数据。(幻读: 数据不真实)

  • 序列化/串行化(serializable)

    并发都没了,事务T1和事务T2不能对同一张表操作。

# 查看事务隔离级别select @@transaction_isolation;# 设置事务隔离级别set global transaction ioslation level 隔离级别

全局锁

全局锁就是对整个数据库实例加锁,即数据库中的所有表都将被加上锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。

# 开启全局锁,全局锁只有读flush tables with read lock;# 解除全局锁unlock tables;

表锁

表锁作用于某一张表,是MyISAM和InnoDB存储引擎支持的方式,是MyISAM的默认锁机制。

# 添加写锁/读锁lock table 表名称 read/write;# 解锁unlock tables;

为表添加写锁后,不能读取表中任何数据。

为表添加读锁后,不能修改表中任何数据。

行锁

InnoDB引擎支持行锁

-- 添加读锁(共享锁), 只能在事务中使用select * from ... lock in share mode;-- 查询时添加写锁(排他锁)select * from ... for update;-- 更新时自动添加写锁update 表名 set xxx = xxx where xxx

行锁的细分,了解一下就好。

记录锁

(Record Locks)记录锁, 仅仅锁住索引记录的一行,在单条索引记录上加锁。Record lock锁住的永远是索引,而非记录本身,即使该表上没有任何索引,那么InnoDB会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。所以说当一条sql没有走任何索引时,那么将会在每一条聚合索引后面加写锁,类似于表锁,但原理上和表锁是完全不同的。

间隙锁

(Gap Locks)仅仅锁住一个索引区间(开区间,不包括双端端点)。在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身。比如在 1、2中,间隙锁的可能值有 (-∞, 1),(1, 2),(2, +∞),间隙锁可用于防止幻读,保证索引间的不会被插入数据。

临键锁

(Next-Key Locks)Record lock + Gap lock,左开右闭区间。默认情况下,InnoDB正是使用Next-key Locks来锁定记录(如select … for update语句)它可以根据场景进行灵活变换:

场景 转换
使用唯一索引进行精确匹配,但表中不存在记录 自动转换为 Gap Locks
使用唯一索引进行精确匹配,且表中存在记录 自动转换为 Record Locks
使用非唯一索引进行精确匹配 不转换
使用唯一索引进行范围匹配 不转换,但是只锁上界,不锁下界

引擎间的锁机制

使用InnoDB的情况下,在执行更新、删除、插入操作时,数据库也会自动为所涉及的行添加写锁(排他锁),直到事务提交时,才会释放锁,执行普通的查询操作时,不会添加任何锁。而当不使用where语句时,行锁会升级为读锁。

使用MyISAM的情况下,在执行更新、删除、插入操作时,数据库会对涉及的表添加写锁,在执行查询操作时,数据库会对涉及的表添加读锁。

-EOF-