9、事务、锁

什么是事务

事务是作为单个逻辑单元执行的一系列操作

多个操作作为一个整体向系统提交,要么执行、要么都不执行,事务是一个不可分割的工作工作逻辑单元

四大特性(ACID)

原子性(A -- Atomicity):原子是参与化学反应中最小的粒子,不能再分割了,也就是说事务就是最小的,不能再分割了,如果把事务都分割了,就会出现问题

一致性(C -- Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态,即数据完整性约束没有被破坏

隔离性(I -- Isolation):事务与事务之间互不干扰

持久性(D -- Durability):通过事务进行操作后的数据,是永久保存的

注意:在实际开发中,经常会打破隔离性,当多个事务共同操作同一张表的时候,一旦打破了隔离性,就会出现安全问题

存储引擎

存储引擎:RDBMS中,决定了数据如何存储,如何获取,如何控制事务,如何控制外键等一系列功能的一套程序

常用引擎:InnoDB,MyIsam

InnoDB与MyIsam的区别

  1. InnoDB支持事务,支持外键;而MyIsam不支持事务,不支持外键
  2. InnoDB由于受到事务和外键的影响,所以对数据的存储以及查询效率偏低;MyIsam相反偏高
  3. InnoDB在存储时,表文件是2个:frm,ibd;而MyIsam是3个文件,分别存储frm,MYD,MYI
  4. InnoDB是MYSQL 5.5之后的默认存储引擎;而MyIsam是5.5之前的默认存储引擎

事务操作

方式一

开启事务start transaction;

提交事务commit;

注意:当事务开启之后,只有执行了commit,数据才会真的改变,如果没有执行commit,数据还原

回滚事务rollback;

方式二

修改默认的提交方式,默认是自动提交,我们要改成手动提交

show variables like '%autocommit%'

set @@autocommit=0(默认为1,自动提交;0,手动提交)

隔离级别

隔离级别 读数据一致性及允许的并发副作用 备注
读未提交(Read uncommitted) 最低级别,只能保证不读取物理上损坏的数据,事务可以看到其他事务没有被提交的数据(脏数据)
读已提交(Read commited) 语句级,事务可以看到其他事务已经提交的数据 Oracle数据库默认
可重复读(Repeatable read) 事务级,事务中两次查询的结果相同 MySql数据库默认
可串行读(序列化Serializable) 最高级别,事务级。顺序执行

隔离等级越高,数据库事务并发执行能力越差,能处理的操作越少。因此在实际项目开发中为了考虑并发性能一般使用读已提交隔离级别,他能避免丢失更新和脏读,尽管不可重复读和幻读不能避免,但可以在可能出现的场合使用悲观锁乐观锁来解决这些问题。

什么是幻读、不可重复度、脏读

1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据,读取到未提交的数据。

2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。

3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读

隔离级别与更新丢失的情况

隔离级别 脏读 不可重复读 幻读 第一类丢失更新 第二类丢失更新
Read uncommitted ×
Read commited × ×
Repeatable read × × × ×
Serializable × × × × ×

设置隔离级别

set session transaction isolation level 隔离级别

查看隔离级别(当前客户端)

select @@tx_isolation

乐观锁

默认存在,什么都不做,就是乐观锁。总是乐观的认为,在维护这个数据的时候,没有其他人来维护。

insert update delete 在执行SQL的同时,才给数据加上了乐观锁。

可能会导致的问题

age:12
A用户:update table set age=32 where id=1
B用户:update table set age=12 where id=1
A用户:select * from table where id=1
这个时候,A用户查看的数据为B修改过的,自己的修改已经被覆盖

悲观锁

总是担心,在自己修改数据的时候,有其他人,将这个数据修改,所以在修改数据之前,提前锁住数据

默认没有开启,需要自己开启

开启命令

#在查询语句后加for update就可以开启(悲观排他锁)
select * from book for update

优缺点

优点:准确性高,更加安全

缺点:效率太低,一旦一个用户执行悲观锁,那么其他用户都无法查看这个表的数据,也无法进行修改,除非执行悲观锁的用户commit

行锁

只会锁住一行数据

select * from book where id=1 for update

表锁

会锁住整个的一个表

select * from book for update

排他锁

A用户在给表加排他锁以后,那么其他用户都无法对表进行操作、查看、加锁

//表级排他锁
lock table book write
//解锁
unlock table

共享锁

所有的用户都可以对表进行加锁,但是,所有的用户都无法对表进行操作,包括上锁的用户

//表级共享锁
lock table book read
//解锁
unlock table

死锁

两个线程自己各自持有自己数据的锁,但互相都想锁住被对方锁住的数据,就产生了死锁

系统检索到死锁,会自动释放一个锁