MySQL笔记10
一、MySQL事务
1.事务定义
是一组操作的集合,是不可分割的工作单位,所有操作作为整体提交或撤销,要么同时成功,要么同时失败。
可由单条SQL语句构成,也可由多条SQL语句构成,用于确保一批任务(操作)的数据完整性
如:
以张三给李四转账1000元为例,逻辑操作包括:判断张三账户是否有1000元、张三账户减少1000元、李四账户增加1000元。若在张三账户扣款后、李四账户加款前程序异常,会导致数据不一致。通过事务可将这三步作为整体,统一提交或撤销,避免数据异常。
2.四大特性
特性 | 英文 | 说明 |
原子性 | atomicity | 事物是不可分割的最小操作单元,要么全部成功,要么全部失败 |
一致性 | consistency | 事物完成后必须保证所有的数据都保持一致状态(一致状态转变到另一个一致状态); |
隔离性 | isolation | 数据库系统提供的隔离机制,保证事物再不受外部并发操作影响的独立环境下运行。(当一个数据库下并发的两个事物同时运行,但是A,B事物的运行不会互相影响 |
持久性 | durability | 事物一旦提交或者回滚,他对数据库中的数据改变就是永久性的。 |
3.操作案例
建表并插入数据:
查询张三账户的余额,确认是否有足够资金:
张三账户余额减1千:
李四的账户余额加1千:
此时,两个人的最终余额为:
出现程序异常的情况:
恢复数据:
在执行李四的语句时,update拼写错误导致程序异常,没执行成功,提交事务后,会造成数据不一致:
4.使用方法
方法1 | 方法2 | 方法3 | 方法4 |
begin | start transaction | set autocommit=1 | set autocommit=0 |
sql语句 | sql语句 | sql语句 | sql语句 |
commit | rollback | commit | rollback | commit | rollback |
方式一:
查看/设置事物提交方式
select @@autocommit;
set @@autocommit=0;
提交事物 commit; ---结束事物
回滚事物 rollback;
注意:以上开启事物是在当前会话临时开启手动提交事物,所以当事物为结束提交是语句仅是在当前会话临时更改数据,新的会话中并没有更改,所以业务操作完成必须手动结束事物操作。 所以当事物业务中因为异常没有正常执行那么就不用 commit 提交而是为了确保数据的完整和一致性我们执行回滚操作 rollback 。
方式二:不更改系统事物默认的提交方式
手动开启事物: start transaction 或 begin
提交事物: commit;
回滚事物:rollback;
savepoint sp1; 未提交前设置任务的保存点
rollback to sp1; 返回到保存点。
5.并发问题
问题 | 描述 |
脏读 | 一个事物读到另一个事物还没提交的数据。 |
不可重复读 | 一个事物先后读取同一条数据,但两次读取的数据不同,称之为不可重复读。 |
幻读 | 一个事物按照条件查询数据时,没有对应的数据行,但是再插入数据时,又发现数据已经存在,好像出现了幻影。 |
脏读:在一个数据库中并发执行事物A 和事物B分别三个操作
当事物A (1)id=1select (2)id=1 update 事物未提交时被事物B中的任务执行查询到了事物A未提交事物中修改的数据则称为脏读;
不可重复读:
事物A中事物开启后先查询一条记录如id=1的相关数据,同时并发事物B中对id=1的数据进行更新并且已经提交,那么此时事物A在以下任务中又再次读取id=1的数据发现在事物A中两次select的结果是不一样的,这种问题就是不可重复读。
幻读:
事物A 先查询数据库中select id=1的数据,并发事物B对数据库完成insert id=1的任务,并且已经通过commit提交,那么事物A在执行id=1(insert) 信息结果会报错,显示主键id已存在数据重复,但是通过任务三再次select查询id=1的结果发现并没有数据(因为已经解决了以上提到的并发事物不可读的情况)所以就出现了一种情况是查询没有数据但是插入显示数据已存在出现了幻影。
事物的隔离级别(事物隔离级别越高,数据越安全,但是性能越低)
隔离级别mysql | 脏读 | 不可重复读 | 幻读 |
Read uncommitted 读未提交 | 可能出现 | 可能出现 | 可能出现 |
Read committed 读已提交 oracle | × | 可能出现 | 可能出现 |
Repeatable read(默认)可重复读 | × | × | 可能出现 |
Serializable 串行化 | × | × | × |
查看事物隔离级别 :
select @@transaction_isolation;
设置事物隔离级别:
set [session|global] transaction isolation level {read uncommitted|read committed|repeatable read |serializable}
6.InnoDB 事务的ACID如何保证
(1)WAL技术
redo log又叫“重做日志”,是存储引擎层 (innoDB) 生成的日志,记录的是"物理级别"上的页修改操作,比如页号x,偏移量y写入了'z'数据,主要目的为了保证数据不丢失,当MySQL发生宕机的时候,可以利用redo log日志进行数据恢复,用于确保事务的持久性
一条SQL更新语句怎么运行:
当有一条记录要更新时,InnoDB 引擎就会先把记录写到 redo log磁盘文件中,此时更新就算结束了
在系统空闲的时候,将上述操作记录更新到磁盘文件中
比如:小店做生意,有个粉板,有个账本,来客点餐了先写粉板,等不忙的时候再写账本
性能不够,缓存来凑,由于CPU的性能远远大于磁盘,为了消除这个鸿沟,引入了两个缓存
Buffer Pool :缓冲池,包含了磁盘中部分数据页的映射,作为访问数据库的缓冲
redo log buffer:redo log本质是磁盘上的日志文件,为题提交读写增加了缓冲区来存放重做日志
WAL:Write-Ahead Logging,预写日志系统,先写日志,再写磁盘,只有日志写入成功,才算事务提交成功的技术思想在MySQL叫做WAL技术,其基本过程如下:
先将原始数据从磁盘中读入到Buffer Pool中
修改Buffer Pool中的数据
生成一条重做日志并写入redo log buffer,记录数据修改后的值
当事务提交时,将redo log buffer中的内容追加磁盘中的redo log文件中
将磁盘日志文件redo log file 内容刷到数据库表中(也称为“脏刷”)
先写 redo log 再刷数据库表”的原因:
数据在 MySQL 中以页为单位存储,事务数据可能分布在不同页中。若直接写入对应页,是随机 IO,性能差。
磁盘顺序 IO性能远高于随机 IO。redo log 通过“顺序追加”的方式写入文件末尾,属于顺序 IO,无效 IO 少;而脏页刷盘以页为单位,是随机 IO,性能差。
InnoDB刷盘策略参数(innodb_flush_log_at_trx_commit):
参数值 | 策略名称 | 执行逻辑 | 数据安全性 | 性能 |
0 | 延迟写 | 事务提交时,不会将redo log buffer中的内容写入page cache,而是每秒由后台线程将redo log buffer中的内容写入page cache。并调用fsync刷盘 | 低。若操作系统崩溃,可能会丢失1秒内的redo log数据 | 高 |
1 | 实时刷盘(默认安全策略) | 事务提交时将 redo log buffer 内容写入 page cache,由操作系统决定何时刷盘。并调用fsync刷盘,确保redo log 持久化到磁盘 | 高。若操作系统崩溃,不丢失 redo log 数据 | 略低 |
2 | 写入页缓存后延迟刷盘 | 事务提交时强制将 redo log buffer 内容刷入磁盘 | 中。若数据系统崩溃,可能丢失未刷盘的 redo log 数据 | 较高 |
(2)redo log相关参数
redo log容量查看:
存储位置:共生成32个 redo log 文件,每个文件的大小等于 1/32 * innodb_redo_log_capacity,redo log 有两种:正在使用的和未被使用的,分别使用 #ib_redoNN 和 #ib_redoNN_tmp其中NN是重做日志文件编号
查看状态:
(3)MySQL CSR——前滚
MySQL在启动时,必须保证redo日志文件和数据文件LSN必须一致, 如果不一致就会触发CSR,最终保证一致
LSN 称为日志的逻辑序列号(log sequence number)在InnoDB存储引擎中,LSN占8个字节,LSN的值会随着日志的写入而逐渐变大,内容包括:
数据页的版本信息。
写入的日志总量。通过LSN开始号码和结束号码可以计算出写入的日志量。
可知道检查点的位置。
前滚过程:
1.做一个事务
begin;
update ....;
commit;
2. begin时会立即分配一个TXID=tx_01.
3. update时,会将需要修改的数据页(dp_01,LSN=101),加载到data buffer中
4. DBWR线程,会进行dp_01数据页修改更新,并更新LSN=102
5. LOGBWR日志写线程,会将dp_01数据页的变化+LSN+TXID存储到redobuffer
6. 执行commit时,LGWR(log write日志写入器)日志写线程会将redobuffer信息写入redolog日志文件中,基于WAL原则,在日志完全写入磁盘后,commit命令才执行成功,(会将此日志打上commit标记)
7. 假如此时宕机,内存脏页没有来得及写入磁盘,内存数据全部丢失
8. MySQL再次重启时,要求redolog和磁盘数据页的LSN是一致的,但是磁盘的值为:LSN=101,而redolog中LSN=102
9.MySQL此时无法正常启动,MySQL触发CSR.在内存追平LSN号,触发ckpt,将内存数据页更新到磁盘,从而保证磁盘数据页和redolog LSN一致,此时MySQL正常启动
10.以上的工作过程,我们把它称之为基于REDO的"前滚操作"
(4)undo——回滚日志
undo(回滚)的作用是保证事务的原子性和隔离性,同时支持‘快照技术’和数据恢复
MySQL 的 InnoDB 存储引擎默认支持多版本并发控制(MVCC),在 MVCC 机制下会有类似快照的概念。当一个事务启动时,InnoDB 会为该事务创建一个一致性视图,这个视图在事务执行期间就相当于一个 “快照”,它记录了事务开始时数据库中数据的状态。通过这个视图,事务可以看到在其启动时的数据版本,而不受其他并发事务对数据修改的影响。
作用:
原子性(Atomicity):通过undo,可以在事务回滚时将数据恢复到修改之前的状态,确保事务要么完全执行,要么完全回滚,避免了部分操作的影响。
隔离性(Isolation)和一致性:在多版本并发控制(MVCC)机制下,`undo log` 用于实现数据的多版本。当一个事务读取数据时,会根据一致性视图(快照)来判断应该读取哪个版本的数据。`undo log` 中保存了数据的旧版本,通过 `undo log` 可以获取到事务开始时数据的状态,从而保证事务读取到的数据是一致的,实现了不同事务之间的隔离性。例如,在可重复读隔离级别下,一个事务在执行期间多次读取同一数据,由于可以根据 `undo log` 读取到事务开始时的数据版本,所以每次读取的结果都是一致的
快照技术:undo提供了事务修改之前的数据状态的快照,这意味着在事务执行过程中,可以回退到之前的状态。这对于实现多版本并发控制(MVCC)非常重要,每个事务可以读取数据的一致版本,而不会受到其他事务的修改影响。
数据恢复:undo日志记录了事务的修改操作和之前的数据状态,当系统发生故障或崩溃时,可以使用undo日志来恢复未提交的事务或未完成的操作。这种数据恢复机制对于数据库的持久性和可靠性非常重要