文章目录
数据库事务的简单理解
事务的概念
-
概念:只为单个逻辑工作的一系列操作,要木完全执行,要木完全不执行,简单来说,事务就是并发控制的单位,是用户定义的一个操作序列。
事务只是一个改变,一些操作的集合,我们可以理解为他就是一个程序的执行单元,我们要通过某些手段让这个执行单元满足这个四个特性,那木我们就可以称之为一个事务,或者说是一个正确的事务,完美的事务。
-
ACID:原子性,一致性,隔离性,持久性
- 原子性 :满足原子操作单元,对数据的操作,要木全部执行,要木全部失败
- 一致性:事务开始和完成,数据都必须保持一致
- 隔离性:事物之间是相互独立的,中间状态时不可见的
- 持久性:数据的修改时永久的
-
场景 :银行转账 A有1000元向B转账200
- 原子性 就是A转出去200元和B收到200元必须都发生,要木都不发生
- 一致性 转账完成后A剩下了800元 而B获得了200元
- 隔离性 A给B转账的同时,C也给B转账100。A和C的操作互不干涉,且B获得金额300元。
- 持久性 转账完毕之后,A和B账户的金额不再会变
事务的隔离级别
背景: 多个事务共同操作数据库
start/begin sql语句 commit rollback
1. 因为并发而可能出现的问题
- 脏读 : A事务对某一个数据进行操作的时候,还未提交。B事务就得到了A事务的结果。
- 不可重复读:A事务在本次事务中,对自己未操作过的数据进行读取,结果出现了不一致的记录或者不存在的记录。(update 和 delete)
- 幻读:A事务在本次事务中,对自己未操作过的数据进行多次读取,第一次读取的时候,记录不存在,而第二次读取的时候,记录出现了(insert)
2. 为了权衡并发和隔离的矛盾,于是定义了事务的四个隔离级别
- 未提交读 read-uncommitted 最低的级别 会发生脏读,不可重复读,幻读
- 已提交的读 red-committed 语句级别的 会发生不可重复读,幻读 (Oracle)
- 可重复读 repeatable-read 事务级别的 会发生幻读 (MySQL)
- 串行化 serializable 最高级别的
如何去实现数据库中的隔离
加锁实现
表锁和行锁 InnoDB引擎提供
从字面上理解就可以了 表锁就是你只要操作数据表中的数据就会导致
- 锁粒度 表锁大于行锁
- 加锁效率 表锁大于行锁
- 冲突概率 表锁大于行锁
- 并发性能 表锁小于行锁
事务隔离级别的实现方式 (LBCC lock-Based Concurrent Control)
1. 共享锁
对select语句加入的锁,是一种可重入锁,是行锁
也被人称之为读锁,对某一个资源加共享锁,自身可以读取资源,其他人也可以读取该资源,但是无法对该资源进行修改,只有当所有的读锁释放完成之后才可以对该资源进行修改。
加锁 select * from table lock in share mode;
释放锁 commit; rollback;
读锁是行锁,锁住的是表中的某一个资源或者某一段资源
2.排他锁
对DML语句默认会加排他锁,是一种不可重入锁,是行锁
顾名思义 : 对某一资源加排他锁,自身可以进行增删改查,其他人无法进行任何操作
排他锁无法与其他锁共存.
默认加锁 : DML语句
手动加锁 : select * from table for update;
释放锁 : commit; rollback;
3.意向共享锁
这个不是我们手动创建的,也不能手动创建, 表锁
充当一种标识的作用, 当表里面有共享锁的时候, 可以理解为这个表的前面就会标识有多少个共享锁. 当一个共享锁释放之后, 对应的意向共享锁也会消失
4.意向排他锁
这个不是我们手动创建的,也不能手动创建, 表锁
充当一种标识的作用, 当表里面有排他锁的时候, 可以理解为这个表的前面就会标识有多少个排他锁. 当一个排他锁释放之后, 对应的意向排他锁也会消失.
其实意向排他锁和意向共享锁是为了, 在其他表锁在对这个表上锁的情况下看看, 是否还有事务对表中的数据进行操作, 若是有的话, 就等待他们执行完毕后再去加表锁. 防止出现问题
锁的算法
对当前数据库中表的记录划分的范围划分
1. 记录锁 (Record Locks)
就是对单个数据进行加锁的行锁 一般是用"="查找某个或者修改单个数据的时候所加的锁就是记录锁
2. 间隙锁(Gap Locks)
就是对多个数据进行加锁的行锁
但是是没有触及到表中记录的范围查找
比如
select * from table where id > 1 and id < 4
它加锁的方式就是间隙锁 加锁的数据段就是(1,4)
select * from table where id > 15
这个加锁的方式是间隙锁 加锁的数据段就是 (10, +无穷大)
3. 临建锁(Next-key-locks)
就是对多个数据进行加锁的行锁
但是是触及到表中记录的范围查找
比如
select * form wher id > 0 and id < 5
加锁的方式就是临建锁, 加锁的数据段就是(-无穷大,7]
事务隔离级别的实现方式(MVCC Multi-Version Concurrent Control)
多版本的并发控制 一种行级锁的变种, 是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别
提高我们的并发量 实其效率更快
**在 MVCC 中事务的修改操作(DELETE、INSERT、UPDATE)会为数据行新增一个版本快照。**这个快快照是对于整个数据库来说的, 但是快照所保存的并不是整个数据库内容,不是复制粘贴将整个数据库中的内容保存到另一个地方 . 而是对于没每一个DML操作之后提交的数据形成一个版本号. 也就是 transaction_id, 这个id记录了每个事务对某个数据的修改.
1.快照读
快照读解决的就是不可重复读的问题
2.当前读
就是为了 避免出现了非一致性问题.
每一个事务开启的时候都会获得InnoDB引擎给与的自增版本号, 事务1的到的版本号(TRX_ID)为103, 事务2得到的版本号为104, 事务三得到的版本号为105, 并且每一个事务得到了最后一个事务提交的数据的版本号(UP_Limit_ID)102, 当他们想要查询A的值的时候, 就会通过UP_Limt_ID中获取得到A的值为100 .
解析事务1.快照读其实就是在同一个事务中没有对这个数据进行修改的话, 它的row_trx_id就为空, 于是它读取数据的时候,读取得永远是版本号为102中数据中内容 所以 W = 100 Q = 100
解析事务2, 读取X的时候 row_trx_id为空,我们直接从UP_Limt_ID中获取到A=100, 得到X=100但是在执行M的时候,事务3已经结束事务并且提交了事务, 而对于修改的操作, 我们理解他要获取最新的A的值(101)进行+1操作, 把A的值设为102的同时并且将row_trx_id = 104, 也就是说在数据库底层已经存储了 版本号为104并且A值102的数据. 当我们进行Y操作的时候, 读取底层的版本号进行对比, 发现row_trx_id = 102并且发现和我们开始事务的时候引擎自动给我们设置的版本号相同,所以读取到Y=102
也就是说在一个事务中,我们进行读取操作的时候, 我们需要先用我们得到的row_trx_id一一对比Undo日志中的数据, 获取之后就是我们所需要的数据. 如果row_trx_id为空, 那木证明我们未在这个事务中进行过增删改查操作, 通过up_limt_id中获得版本号,然后通过这个版本号来对比, 最终查到对应的数据.
up_limt_id 是我们进行事务之前获得到的最后一次提交的版本号
TRX_ID 是我们开启事务之前, InnoDB引擎自动给我们分配的版本号
row_trx_id 是我们事务之中若是进行了增删改操作, 那木我们自己的事务要认, 于是从row_trx_id中的版本号中获取数据.