事务
事务(Transaction)是由一系列对系统中数据进⾏访问与更新的操作所组成的⼀个程序执行逻辑单元。
1.1事务的语法
- starttransaction;begin;
- commit;使得当前的修改确认
- rollback;使得当前的修改被放弃
1.2事务的ACID特性
-
原⼦性(Atomicity)
指事务必须是⼀个原子的操作序列单元。事务中包含的各项操作在⼀次执⾏过程中,只允许出现两种状态之一。
(1)全部执行成功(2)全部执行失败事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执⾏过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发⽣一样。
-
⼀致性(Consistency)
指事务的执⾏不能破坏数据库数据的完整性和一致性,
一个事务在执⾏之前和执行之后,数据库都必须处以⼀致性状态。
-
隔离性(Isolation)
指在并发环境中,并发的事务是互相隔离的。也就是说,不同的事务并发操作相同的数据时,每个事务都有各自完整的数据空间。
⼀个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰的。
隔离性分4个级别,READUNCOMMITTED(未提交读)READCOMMITTED(已提交读)REPEATABLEREAD(可重复读)SERIALIZABLE(序列化)。
-
持久性(Duration)
指
事务⼀旦提交后,数据库中的数据必须被永久的保存下来。
即使服务器系统崩溃或服务器宕机等故障。只要数据库重新启动,那么一定能够将其恢复到事务成功结束后的状态。
1.3事务的并发问题
事务之间的相互影响分为几种,分别为:脏读,不可重复读,幻读,丢失更新
1.脏读
脏读意味着一个事务读取了另一个事务未提交的数据,而这个数据是有可能回滚的;
2.不可重复读
不可重复读意味着,在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。
3幻读(虚读)
幻读,是指当事务不是独立执行时发生的一种现象,例如A给全部的员工加了1000元的工资,在此期间,B加入了一名新的员工,结果A的事务作用在B的对象上,导致新员工和老员工一样加了1000工资,在B看来是虚幻的。
幻读和不可重复读的结果相似,幻读体现在修改数据的同时对数据量进行操作所引发的问题;而不可重复读是由于原有数据内容的更替造成的,两者有差别。
4.丢失更新
两个事务同时读取同一条记录,A先修改记录,B也修改记录(B是不知道A修改过),B提交数据后B的修改结果覆盖了A的修改结果。
1.4事务隔离级别
为解决上述并发问题,提出了事务隔离概念。
隔离级别 | 脏读 | 不可重复读 | 幻读 | 丢失更新 | 并发模型 | 更新冲突检测 |
---|---|---|---|---|---|---|
未提交读:ReadUncommited | 是 | 是 | 是 | 是 | 悲观 | 否 |
已提交读:Readcommited | 否 | 是 | 是 | 是 | 悲观 | 否 |
可重复读:RepeatableRead | 否 | 否 | 可能 | 否 | 悲观 | 否 |
可串行读(序列化):Serializable | 否 | 否 | 否 | 否 | 悲观 | 否 |
使用方法:setsessiontransactionisolationlevel隔离级别
检测方法:mysql:select@@tx_isolation;
mysql8以后:select@@transaction_isolation;
1.5不同的隔离级别的锁的情况
未提交读:在读数据时不会检查或使用任何锁。因此,在这种隔离级别中可能读取到没有提交的数据。
已提交读:只读取提交的数据并等待其他事务释放排他锁。读数据的共享锁在读操作完成后立即释放。已提交读是SQLServer的默认隔离级别。
可重复读:像已提交读级别那样读数据,但会保持共享锁直到事务结束。
可串行读:工作方式类似于可重复读。但它不仅会锁定受影响的数据,还会锁定这个范围。这就阻止了新数据插入查询所涉及的范围。
总结
- 未提交读(RU):有行级的锁,没有间隙锁。读取到没有提交的数据。
- 已提交读(RC):有行级的锁,没有间隙锁,读不到没有提交的数据。
- 可重复读(RR):有行级的锁,也有间隙锁,每次读取的数据都是一样的,并且可能有幻读的情况。
- 可串行读(S):有行级锁,也有间隙锁,读表的时候,就已经上锁了
1.6隐式提交(了解)
DQL:查询语句句
DML:写操作(添加,删除,修改)
DDL:定义语句句(建库,建表,修改表,索引操作,存储过程,视图)。DDL都是隐式提交。
DCL:控制语⾔言(给⽤用户授权,或删除授权)
隐式提交:执⾏行行这种语句句相当于执⾏行行commit;
1.7关于锁
悲观锁、乐观锁,表锁、行锁,共享锁、排他锁
乐观锁
相当于乐观的人,总是想着事情往好的方向发展
乐观锁是指对数据库进行 增、删、改 操作时,想法很乐观,认为这次的操作不会导致冲突,在操作数据时,不进行加锁处理,而在进行更新后,再去判断是否有冲突
使用场景
适用于写比较少的情况下,多用于读
实现方式
数据库没有实现功能,需要开发者实现:
- 版本号机制
在数据表中加上一个数据版本号version
字段,表示数据被修改的次数,当数据被修改时,version
值会加一。要update
该条记录时,先读取该条记录,在提交更新时,若刚才读取到的version
值为当前数据库中的version
值相等时才更新,否则重试更新操作,直到更新成功。 - CAS算法实现
悲观锁
悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过 获取锁 才能进行操作
与java中的synchronized类似,所以悲观锁需要耗费较多的时间。
应用场景
一般多写的场景下
实现方式
由数据库实现
表锁
MyISAM 存储引擎不支持事务,使用 表锁,就是锁一整张表,更新一条记录就要锁整个表,导致性能较低,并发不高。
不会死锁
行锁
InnoDB 存储引擎支持事务,使用 行锁,在事务中,修改哪行,就只锁定哪行
行锁并不是直接锁记录,而是锁索引
索引分为 主键索引 和 非主键索引 两种:
- 如果一条sql 语句操作了主键索引,Mysql 就会锁定这条主键索引;
- 如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引
- 如果没有索引,InnoDB 会通过隐藏的聚簇索引来对记录加锁。
也就是说:如果不通过索引条件检索数据,那么InnoDB将对表中所有数据加锁,实际效果跟表锁一样。因为没有了索引,找到某一条记录就得扫描全表,要扫描全表,就得锁定表。
共享锁
又称为读锁,简称S锁,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改,要想修改就必须等所有共享锁都释放完之后。
select 语句
select可以通过 select ... lock in share mode
加共享锁
排他锁
又称为写锁,简称X锁,排他锁就是不能与其他锁并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。
delete、insert、update 语句
都会自动给涉及到的数据加上排他锁
select 语句
select语句默认不会加任何锁,所以可以查询被 排他锁 锁住的记录
select可以通过 select ...for update
加排他锁