文章目录
>> 什么是事务
产生事务就会产生锁;
~MySQL
MySQL中,事务是一个最小的不可分割的工作单元,事务里面可以包含多条SQL语句,事务中的语句,要么全部执行成功,事务提交,要么有一条语句执行失败,事务回滚到开始事务之前的状态;事务能保证业务的完整性;
MySQL中是默认开启事务(自动提交)的,也就是说,如果不显示的开始一个事务,每个语句都会被当做一个事务执行提交操作;
所以当我们去执行一条sql语句时,效果会立即体现出来,不能回滚;这时如果执行两个语句,第一个执行成功了,第二个执行失败,我们不能回滚操作,这就会出现数据不一致问题;
我们可以手动开启一个事务,然后再执行多条语句,若其中一条执行失败,可以执行回滚操作;若都执行成功,可以commit提交;这时候事务就结束了,就不能再回滚了;
- 事务开启:
1、修改默认提交:set autocommit=0;
2、begin;
3、start transaction;
- 事务提交:
commit;
,将修改的数据持久保留; - 事务回滚:
rollback;
,撤销语句的执行效果;
~ Oracle
查看事务:select * from v$transaction;
事务采用隐性的方式,起始于session的第一条DML语句;
事务结束于:
(1)commit或rollback;
(2)DDL语句被执行(提交);
(3)DCL语句被执行(提交);
(4)用户退出SQLplus:正常退出是提交,非正常退出是回滚;
(5)机器故障或系统崩溃(回滚);
(6)shutdown immediate(回滚);
DML:insert、update、delete、merge;
DDL:create、alter、drop、truncate;
DCL:grant、revoke;
>> 事务的四大特征 - ACID
1、原子性(Acomicity )
:一个事务必须被视为一个不可分割的最小的工作单元,整个事务中的所有操作,要么全部执行成功提交,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作;这就是事务的原子性;
场景: 银行转账,A-100,B+100,同时成功或同时失败;
2、一致性(Consistency)
:
解释1:数据库总是从一个一致性状态 转换到另一个一致性状态;比如从A账户转账给B账户,不管操作是否成功,两个账户的存款总额不变;
解释2:一个查询的结果,必须与数据库在查询开始时的状态一致;(读不等待写,写不等待读)
场景: 用户A查询某个表的数据时,九点开始,此时表中有100万条记录,需要查询十分钟,九点五分的时候,用户B向表中插入了一条数据,此时表中有100万零1条记录,但是用户A查询出来的还是100万条记录;就是说查询操作开始执行时,表中有多少条记录,查询出来的就有多少条;
3、隔离性(Isolation)
:通常来说,事务之间互不干扰;一个事务所做的修改在最终提交之前,对其他事务是不可见的;隔离的程度通过设置隔离级别来确定;
场景: 两个session,一个查询,一个更新,更新的操作没有提交之前,查询所看到的数据都是没有更新之前的,两个session互不影响;
4、持久性(Driability)
:事务一旦结束(commit),其所做的修改就会永久保存到数据库中,就不可再回滚;数据库通过日志能够保证事务的持久性;
场景: 事务提交之后是不可逆的,提交数据是将内存的数据刷新到硬盘上,Oracle靠“rudo”日志实现;事务提交时,rudo会先将提交的操作记录到日志文件中,然后才开始往磁盘刷数据的操作,比如批量insert,执行到一半的时候,操作中断了,Oracle重启恢复之后,会从rudo中找到日志,继续未完成的事;
>> 锁 (oracle)
1、锁的用途: 只有有事务才有锁,用于保证数据的完整性和一致性;
2、锁的分类:
共享锁
:排斥其他锁,不排斥共享锁;排他锁
:独占,排斥其他共享锁、排它锁;
3、锁的类型:
DML锁
:data locks,数据锁,用于保护数据的完整性;
TX
:行级锁;
TM
:表级锁;DDL锁
:dictionary locks,数据字典锁,用于保护数据库对象的结构,如表、索引等的结构定义;system锁
:internal locks and latches,保护数据库的内部结构;
4、加锁模式:
在做DML (insert/update/delete)时自动加锁,使用select ... for update
加锁;
A用户select * from user where username = 'zxj' for update;
执行时这条记录会被锁定;这时用户B要update这条记录,就会进入等到状态,等A提交释放锁之后,就会立即执行B的update操作;
Oracle里面不存在死锁,有的话Oracle会自动解决; 若是A一直不释放锁,B将一直处于等待状态,若B的操作是点击页面的更改按钮,就会出现页面一直刷不出来,会被认为出现死锁,不友好;
解决这个问题的方案:
(1)select * from user where username = 'zxj' for update nowait;
,不等待,B执行操作,会立即返回提示信息:资源正忙…
(2)select * from user where username = 'zxj' for update wait 5;
,等待5秒,5秒之后,返回提示信息:资源被占用…
(3)select * from user where username = 'zxj' for update skip locked;
,跳过被锁定的记录,返回其他没有锁定的记录;
5、暴力解锁: 若select ... for update
的锁占用的时间太长,可以通过管理员用户kill掉创建锁的session;
(1)查看v$lock
表,查看哪个sid
占用的时间长;
(2)根据查出来的sid
,去v$session
表中查找session:select sid, serial# from v$sesssion where sid = ...;
(3)alter system kill session 'sid的值, serial#的值';
>> 事务隔离级别
和Java程序使用对象锁机制进行线程同步类似,数据库管理系统采用数据库锁机制
保证事务的隔离性;当多个事务试图对相同数据进行操作时,只有持有锁的事务才能操作数据,知道一个事务提交之后,其他事务才有机会对数据进行操作;
事务隔离级别是数据库提供的自动锁机制;
查看事务隔离级别:
mysql 8.x:
select @@global.transaction_isolation; 系统级别的;
select @@transaction_isolation; 会话级别的;
mysql 5.x:
select @@global.tx_isolation; 系统级别的;
select @@tx_isolation; 会话级别的;
修改事务隔离级别:
set global transation isolaction level read uncommitted;
事务的隔离级别:
read uncommitted
:读未提交;read committed
:读已提交;repeatable read
:可重复读;MySQL默认级别serializable
:串行化;
较低级别的隔离,通常可以执行更高的并发,系统的开销也更低;事务隔离级别越高,数据库性能越差:serializable
级别最高,并发最低,性能最差;
1、read uncommitted:读未提交 - 脏读
事务中的修改,即使没有提交,修改的数据对其他事务也是可见的;
脏读
:一个事务读取到了另一个事务B修改未提交的数据;
如果事务A读取到了事务B修改未提交的更改数据,并在这个数据的基础上进行操作,若这时事务B回滚,那么事务A读取到的数据是不被承认的;
2、read committed:读已提交 - 不可重复读
一个事务从开始直到提交之前,所做的修改对其他事务都不可见;一个事务只能读取到另一个事务已经提交的更改数据
;
但是会出现不可重复读现象
:执行两次同样的查询操作,可能会得到不一样的结果;
事务A查询账户余额为100,这时事务B取出10元,将余额改为90元,提交事务,然后事务A再次读取账户余额,两次读取到的数据不一样;
3、repeatable read:可重复读 - 幻象读
该级别保证了同一个事务中,多次读取同样的记录的结果是一样的;
但是在统计数据的事务中,可能发生幻象读;
幻象读:一个事务A读取不到另一个事务B新增的并已经提价的记录,但是新纪录又会影响到事务A的操作;
(1)事务A查询表中有5条记录,这时事务B向表中插入一条记录,并提交,事务A再次查询记录数还是5条,这时事务A向表中插入同样一条记录,会报错:显示该记录以存在,这就出现了幻象读;
(2)事务A统计所有账户总额是100,这时事务B新增账户,余额是1,然后提交,账户A再次统计余额时是101,幻象读;
不可重复读
指的是读到了其他事务已经提交的更改或删除
的数据;
幻象读
指的是读取到了其他事务已提交的新增
数据;
4、serializable:串行化;
一个事务A操作一个表的时候,其他事务B不能对该表执行写操作,其他事务B会进入串行化状态(排队),等待事务A操作完提交之后,事务B的写操作会自动执行;(没有超时的情况下)
>> 数据库并发的问题
-
脏读
:事务A读取到了事务B未提交的数据,使用之后,事务B执行回滚操作,使得事务A读取到的数据失效; -
不可重复读
:事务A读取到了事务B修改或删除的数据,使得事务A在事务B的操作执行前后两次相同的读取操作,读取到的数据不一致; -
幻象读
:事务A读取不到事务B已经提交的新增数据,但是新增数据又会影响事务A的操作; -
第一类丢失更新
:事务A回滚
事务时,覆盖事务B已提交的数据;
事务A开启事务,查询账户余额为1000,事务B开启事务,查询余额1000,然后存入100将余额改为1100,并提交,事务A取出100,将余额改为900,操作失败,事务A回滚,余额恢复为1000;这时事务B存入的100元丢失; -
第二类丢失更新
:事务A提交
事务时,覆盖事务B已经提交的数据;
事务A查询余额1000,事务B查询余额为1000,取出100,余额改为900,提交事务;事务A存入100,提交事务,余额改为1100;