Mysql数据库ACID特性-AID如何保C
数据库ACID-AID如何保C
- ACID介绍
- 原子性 A
- 持久性 D
- 隔离性 I
- 一致性 C
- AID实现方式
- 原子性 A / 持久性 D
- undo log
- redo log
- 隔离性 I
- 隔离级别
- MVCC
- 锁
- 全局锁
- 表锁
- 元数据锁(MDL)
- 意向锁 (解决行锁和表锁冲突问题)
- 行锁
ACID介绍
原子性 A
事务是一系列操作数据库操作的集合,要么全部提交,要么全部回滚。
持久性 D
当事务提交成功,数据库数据就会永久改变。
隔离性 I
隔离性是多个事务并发情况下,事务和事务之间互不影响
一致性 C
在AID的手段下,一致性指事务将数据库从一种状态转变为另一种一致的状态。
一致性就是数据是有意义的,业务逻辑一致,数据完整
事务A 事务B同时开启
事务A 张三给李四转账1000
begin/start transaction;
update tb_user set money = money - 1000 where name='张三';
update tb_user set money = money +1000 where name='李四';
commit/rollback;事务B 王五给李四转账一千
begin/start transaction;
update tb_user set money = money - 1000 where name='王五';
update tb_user set money = money +1000 where name='李四';
commit/rollback;
AID实现方式
原子性 A / 持久性 D
undo log
事务执行过程中发生错误/宕机,那么直接用undo log进行回滚操作,保证事务的原子性
redo log
如果事务已经提交,用redo log进行重做,保证事务的持久性
通过预写式日志,undo log保证原子性,redo log保证持久性,设置隔离级别,保证并发事务进行的时候,保证数据一致性。
隔离性 I
隔离级别
设置隔离级别,保证并发事务进行的时候,保证数据一致性。
MySQL有四个隔离级别,读未提交,读提交,可重复读,串行化;
未提交读: A事务读取B事务没有提交的数据。
已提交读: A事务读取B事务已提交的数据。
可重复读: A事务先读取,B事务修改数据并且提交后,A事务再读取数据,A事务读取的还是之前的版本。
串行化:不管读操作还是写操作都是会对数据上锁。给间隙加上共享锁或者排他锁。
脏读: A事务读取B事务修改完,但是没有提交的数据,B事务发生回滚,数据前后不一致
不可重复读: A事务进行读取,然后B事务修改数据,当B事务提交数据后,A事务再度读取数据,不一致。
幻读:A事务进行查询,B事务再进行插入或删除,A再查询,发现多了或者少了一条 。
MVCC
MVCC:使得InnoDB的事务隔离级别下一致性读没有冲突,在并发读写数据库时,可以做到在读操作时不用阻塞写操作。InnoDB默认级别是可重复读,通过MVCC实现;
MVCC的实现,需要依赖于数据库中的 三个隐式字段,undo log,readView
隐藏字段
| 隐藏字段 | 描述 |
|---|---|
| row_id | 每行的唯一唯一标识,如果表结构没有指定主键,将生成该隐藏字段 |
| trx_id | 每次对某条记录进行修改时,都把对应的事务id赋值给trx_id隐藏列 |
| roll_pointer | 每次对某条记录进行修改,会把旧版本写入undo log,相当于一个指针,可以通过它来找到该记录修改前数据。 |
undo log 回滚日志
undo log 版本链 不同事务或者相同事务对同一条记录进行修改,会导致该记录的undo log生成一条记录版本,> > 链表的头部是最新的旧记录,尾部是最早的旧记录
| 事务1111 | 事务2222 | 事务3333 | 事务4444 |
|---|---|---|---|
| begin | begin | begin | begin |
| inset id=1 | ----- | ----- | ----- |
| commit | ----- | ----- | ----- |
| ----- | ----- | ----- | ①select id =1 |
| ----- | update set name=‘李四‘ id =1 | ----- | ----- |
| ----- | ----- | ----- | ----- |
| ----- | commit | ----- | ----- |
| ----- | ----- | update set name=‘王五‘ id =1 | ----- |
| ----- | ----- | ----- | ②select id =1 |
| ----- | ----- | commit | ----- |
| 地址 | row_id主键 | name | age | trx_id | roll_pointer |
|---|---|---|---|---|---|
| 0X0003 | 1 | 王五 | 12 | 事务3333 | 0X0002 |
| 0X0002 | 1 | 李四 | 12 | 事务2222 | 0X0001 |
| 0X0001 | 1 | 张三 | 12 | 事务1111 | null |
readview (读视图)
readview 是快照读sql执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id
| 核心字段 | 描述 |
|---|---|
| m_ids | 当前活跃的事务ID集合 |
| min_trx_id | 最小活跃事务ID |
| max_trx_id | 预分配事务ID ,当前最大事务ID+1(因为事务ID是自增的) |
| creator_trx_id | readview创建者的事务ID |
trx_id == creator_trx_id 可以访问该版本 说明数据是当前这个事务更改的
trx_id < min_trx_id 可以访问该版本 说明数据已经提交
trx_id > max_trx_id 不可以访问该版本 说明事务是在readView生成后才开启
min_trx_id <= trx_id <= max_trx_id 如果trx_id不在m_ids中是可以访问该版本的 说明数据已经他提交
read committed 在事务中每一次执行快照读时生成ReadView
repeatable read 仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView
①select id =1
m_ids[事务2222,事务3333,事务4444]
min_trx_id[事务2222]
max_trx_id[事务5555]
creator_trx_id[事务4444]
②select id =1
m_ids[事务3333,事务4444]
min_trx_id[事务3333]
max_trx_id[事务5555]
creator_trx_id[事务4444]
锁
锁 并发访问某一个资源的机制
全局锁
锁定数据库中所有表
锁定 flush tables with read lock
解锁 unlock tables;/断开连接
表锁
锁定操作的表,发生冲突的的概率高,并发低 (表锁,元数据锁,意向锁)
锁表 lock tables itheima read/weite
解锁 unlock tables itheima /关闭客户端
表共享读锁 A上了读锁, A能读不能写,所有人都不能写入 ,只能读
表独占写锁 A上了锁,A能读写,B不能写入,也不能读
元数据锁(MDL)
系统自动加锁 为了避免 DML和DDL冲突,保证读写的正确性
DDL 定义语言,定义数据库对象 数据库,表,字段
DML 操作语言,数据进行增删改
metadata lock是表级锁,是在server层加的,适用于所有存储引擎。
··所有的dml操作都会在表上加一个metadata读锁
··所有的ddl操作都会在表上加一个metadata写锁
····读锁和写锁之间相互阻塞,即同一个表上的dml和ddl之间互相阻塞。
····写锁和写锁之间互相阻塞,即两个session不能对表同时做表定义变更,需要串行操作。
····读锁和读锁之间不会产生阻塞。也就是增删改查不会因为metadata lock产生阻塞,可以并发执行,
····日常工作中大家看到的dml之间的锁等待是innodb行锁引起的,和metadata lock无关。
意向锁 (解决行锁和表锁冲突问题)
意向共享 (is) 和read兼容,writer互斥 select lock in share mode
意向排他 (ix) 和read互斥,writer互斥 insert/update/delete/select for update
行锁
InnoDB引擎是基于索引组织的,行锁通过索引上的索引项来实现,而不是对记录加锁
共享锁S 允许一个事务去读取一行 (共享锁和共享锁兼容)(共享锁和排他锁不兼容)
排他锁X 允许获取排他锁的事务更新数据 (排他锁和共享,排他都不兼容)
--select 不加锁
--select lock in share mode 共享锁
--select for update 排他锁
--insert/update/delete 排他锁
--InnoDB的行锁针对的是索引加的锁,不通过索引条件检索数据,那么Innodb将对表中的所有记录加锁(记录加锁,所有记录X)
间隙锁 锁定索引记录间隙(不含记录,确保索引记录间隙不变,防止其他事务往这个间隙里insert,产生幻读,在可重复读下支持)
----间隙锁的唯一目是防止其他事务插入间隙。间隙锁可以共存,一个事务采用间隙锁不会阻止另一个事务在同一间隙采用间隙锁
临键锁 行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙GAP, 在可重复读下支持
**索引上的的等值查询(唯一索引),给不存在的记录加锁,优化为间隙锁
id(1235)(查询4)(锁3-5)(insert 4)
INNODB itcast tb_user PRIMARY 1650509289496 RECORD S,GAP GRANTED 5
**索引上的的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,为间隙锁
age=19 (2,19 3,19) 查询19 锁定所有19之间的间隙
临键锁 2,3锁住 , 2之前间隙,3之前间隙加锁 5之前间隙加锁 ,5不加锁
INNODB itcast tb_user TABLE IS GRANTED
INNODB itcast tb_user idx_user_age RECORD S GRANTED 19, 2
INNODB itcast tb_user idx_user_age RECORD S GRANTED 19, 3
INNODB itcast tb_user idx_user_age RECORD S,GAP GRANTED 20, 5
INNODB itcast tb_user PRIMARY RECORD S,REC_NOT_GAP GRANTED 3
INNODB itcast tb_user PRIMARY RECORD S,REC_NOT_GAP GRANTED 2
**索引上范围查询(唯一索引)会访问到不满足条件的的第一个为止 锁住符合的数据 和 数据向前的间隙
--select * from tb_user where id>3 lock in share mode;
INNODB itcast tb_user PRIMARY RECORD S GRANTED supremum pseudo-record 正无穷
INNODB itcast tb_user PRIMARY RECORD S GRANTED 5
面试题:数据库的ACID
1.原子,持久,隔离,一致
2.原子:一个事务对数据进行操作,要么全部提交,要么全部退回。
··持久:子只要事务提交就是对数据库进行修改。
··隔离:多个事务,保证各个事务相对隔离,不影响其他事务。
··一致:一致是数据从一个一致性状态转移到另一个一致性状态,保证业务数据有意义
··其中原子,持久,隔离是达到一致性的手段,一致性是目的
3.原子:undo log
··持久:redo log
····当你修改一个数据,首先去buffer pool(缓冲池)中找到这个数据,如果没有先去磁盘中找到这个数据,然后放进
····缓冲池中,在更新Buffer Pool中数据之前,先把原数据放进undo log中一份,然后对当前缓冲池中的页进行修改,
--····修改完的数据放redo log buffer中,当事务提交 重做日志缓冲区的数据刷进redo log中
····事务进行中发生错误或者回滚,通过 undo log 进行保证数据库的原子性
····事务提交后放生错误,通过redo log进行重做,保证数据库的持久性
··隔离性:多个事务进行读写的相对隔离通过MVCC和锁
····脏读:一个事务读取到了另一个事务没有提交的数据
····重复读:A事务进行查询,B事务进行修改操作进行提交,然后A再查询,发现两次数据不一致。:
····幻读:A事务进行查询,B事务进行删除或者新增进行提交,A事务查询发现多了或者少了几条
····隔离级别
······读未提交:啥也没解决
······读已提交:解决了脏读
······可重复读:解决了脏读,重复读 (通过MVCC极大避免幻读,A先查询,B新增,A再修改,A查询多一行)
······串行化:解决了脏读,重复读,幻读······读未提交允许脏读的存在,不用进行版本控制
······串行化隔离级别需要对所读取的行都加共享读锁,不存在多线程并发问题。
······读已提交和可重复读是使用了MVCC,用于处理读-写并发冲突问题,从而达到再任何时候读操作都是非阻塞状态.
·········MVCC是通过隐藏字段,undo log日志 , readView 实现的
············隐藏字段和undo log
··············当一个事务开启(mysql就会为事务分配一个事务ID,这个ID是递增的)
··············这个事务修改某一行(where id =1)数据之后,对(id=1的数据)行上排他锁,
··············旧版本的数据会拷贝到undo log中,
··············新版本的数据的回滚指针roll_ponter会指向undo log中的就数据上,
··············依次形成一种链式结构,需要回滚时,根据隐藏列可以找到改动前的的数据进行回滚。
··············提交后事务放开id=1的排他锁
············readView
··············当事务读取一条数据,MVCC会生成快照,也称为读视图,其中包含
··············1.creator_trx_id:当前读视图的事务ID
··············2.trx_ids:现存活跃事务的集合【122222,1222223.,131111】
··············3.现存最小事务ID
··············4.生成当前读视图时,要分配给下一个事务的ID············有了,隐藏字段,undo log ,读视图readview 那么咱们怎么判断
··············1.当事务出现select时,生成readview
··············2.判断数据行trx_id与craterot-trx_id是否相等
··················相同就是这行数据就是这个事务修改的,可以读取最新数据
··················不相同就是要查询的数据不是这个事务修改的,是被其他事务修改的,那咱们就要继续判断了
··············3.判断数据行trx_id是不是小于现存活跃事务的最小事务ID
··················小于就是数据行的事务在当前readview生成的时候已经结束了,可以读取到最新版本的数据
··················不小于代表该数据行的事务还在执行,就得继续判断
··············4.判断数据行trx_id是不是小于生成当前读视图时,要分配给下一个事务的ID
··················大于等于要分配给下一个事务的ID,事务是读视图生成后修改的数据,读视图读取不到最新数据
··················小于,代表改动的数据在现存最小事务和生成当前读视图时,要分配给下一个事务的ID之间继续判断
··············5.数据trx_id在不在现存的事务集合中
··················不在,代表事务已经提交了,可以访问最新数据
··················在,代表事务还在进行,不可以访问
··············如果当前行数据不满足,依照这个判断继续在链表中往下寻找数据············那读已提交和重复读这俩个隔离级别的实现方式不同点是什么
············读已提交 : 每次select,都生成一个快照读
············重复读 : 每次开启事务第一个select语句才是快照读的地方,其他select复用第一个select
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
