赞
踩
概念:数据库的事务(Transaction)是一种机制、一个操作序列,包含了一组数据库操作命令。事务把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行,因此事务是一个不可分割的工作逻辑单元。
就我的理解来说:简单的说事务就是一组同时执行成功或者同时执行失败的数据库操作命令。
START TRANSACTION READ ONLY
语句开启一个只读事务【拓展】数据库中的临时表(出现在MySQL的3.23版本以后)
- 作用:临时表有时候对于保存临时数据非常有用。临时表可以和普通表一样的进行操作,并且在当前的终端会话结束后被删除。
- 操作:
- 表创建:
CREATE TEMPORARY TABLE tableName
- 表删除:
DROP TABLE tableName;
START TRANSACTION READ WRITE
语句开启一个读写事务或者直接使用 BEGIN、START TRANSACTION
开启事务中最重要的特性其实时一致性,一致性是开始事务的最终目的,而原子性、隔离性和持久性都是实现一致性的手段
先将修改内容写入到日志中,然后再同步到系统磁盘中,InnoDB就是通过这种方式实现的,日志包括undo log和redo log来实现事务;其中数据落盘成功前也可以进行数据的改动。
MySQL的InnoDB的事务就是使用这种方式实现的。
在日志落盘成功后才进行数据的修改;必须落盘成功再进行数据修改。
OceanBase数据库就使用的这种方式实现的。
使用写时复制的机制实现,再进修改操作时对修改数据做一个复制操作,在复制区域进行修改,修改完成后再进行内容的替换。
安卓开发中SQLLitte就是通过这种方式实现的。
innodb_log_file_size
参数进行设置。ib_logfile0和ib_logfile1
;最多可以设置为100innodb_log_files_in_group
参数进行设置log buffer
:日志缓冲区
log buffer
默认大小为 16KB,区域被分为大小为512字节的块,方便写入磁盘中的redo日志innodb_log_buffer_size
来进行设置Log Sequence Number(LSN):日志序列号,规定初始的lsn值为8704
- 查看系统中的LSN的值:
SHOW ENGINE INNODB STATUS\G
Log sequence number
:当前所有的redo日志最大的日志序号Log flushed up to
:已经进行了刷盘的redo日志的的日志序号- 使用
Log sequence number
和Log flushed up to
进行比价来保证为未刷盘的记录不会被覆盖
log buffer
空间不足时roll_pointer
:roll_pointer
本质上就是一个指向记录对应的undo日志的一个指针trx_id
:一个事务的起点不是从begin/start transaction
开始的,当事务中执行第一个写语句,事务才真正的开始,才会向MySQL来申请事务iddelete_mask
:行记录的删除标记next_record
:指向下一个记录的指针PAGE_FREE
的属性它指向由被删除记录组成的垃圾链表中的头节点next_record
属性组成一个单向链表,我们把这个链表称之为正常记录链表Insert语句
TRX_UNDO_INSERT_REC
类型Delete语句
TRX_UNDO_DEL_MARK_REC
类型Update语句
TRX_UNDO_DEL_MARK_REC类型 + TRX_UNDO_INSERT_REC类型
TRX_UNDO_UPD_EXIST_REC
类型并发事务带来的问题:
隔离级别:读未提交(read uncommitted)、读已提交(read committed)、可重复度(Redpeatable read)、可串行化(serializable)
隔离级别 | 脏读(DIrty Read) | 不可重复读(NonRepeatable) | 幻读(Phantom Read) |
---|---|---|---|
读未提交(Read uncommitted) | 可能 | 可能 | 可能 |
读已提交 (Read committed) | 不可能 | 可能 | 可能 |
可重复读 (Repeatable read) | 不可能 | 不可能 | 可能 |
可串行化 (Serializable) | 不可能 | 不可能 | 不可能 |
set tx_isolation='read-uncommitted';
set tx_isolation='read-committed';
set tx_isolation='repeatable-read';
set tx_isolation='serializable';
InnoDB的读已提交和不可重复读就是通过MVCC机制实现的;
MVCC的实现借助了两个对象来实现,分别是undo日志版本链和read-view一致性视图
undo日志版本链
一致性视图 read-view
举个MVCC进行比较例子(当前的隔离级别为可重复读)
update employees_s set name = 'Tom' where id = 2;
生成新的undo日志,版本连如下:新的undo日志中事务为100,name字段修改为了Tom,并添加到了undo日志版本链的中
【步骤三】事务B执行更新操作,update employees_s set name = 'Jim' where id = 3;
生成新的undo日志,版本链如下:
新的undo日志中事务为200,name字段修改为了Jim,并添加到了undo日志版本链的中
【步骤四】事务C进行查询操作,select * from employees_s where id = 2;
此时的事务C生成read-view一致性视图
json { 'uncommitted_trx_id_list':[100,200], 'max_trx_id': 300 }
所以根据一致性视图和当前undo日志版本链的比较规则,从undo日志的头节点进行遍历,通过row_id找到对应的undo日志,对于事务C来说,row_id为2,trx_id为100在事务C一致性视图的uncommitted_trx_id_list
中,所以这条undo日志对于事务C是不可见的,所以继续向下遍历,继续找row_id为2的undo日志,找到row_id为2,trx_id为0的undo日志,0 事务id不在事务C的uncommitted_trx_id_list
中,这样来说对对于事务C是可以见的,就读到了当前的结果2,Lucy,18
【步骤五和步骤六】事务A进行提交,当前的undo日志版本链如下:
【步骤七】事务D进行查询操作,select * from employees_s where id = 3;
此时事务D会生成一致性视图
json { 'uncommitted_trx_id_list':[200], 'max_trx_id': 300 }
所以根据一致性视图和undo日志版本链的比较规则,分别找到row_id为3两条undo日志,分别对应的事务id为300和0对应的改动日志,200不在事务D的uncommitted_trx_id_list集合中,数据对于事务D是可见的,读取到的数据就是3,Jim,24
【步骤八】事务C进行查询,还是依旧使用步骤四中生成的一致性视图,和当前的版本链进行比较,索引最终找到的两条undo日志为row_id=2,trx_id=0和row_id=3,trx_id=0的undo日志,结果为(2,Lucy,18),(3,Herry,24)
【步骤九】事务B提价事务后,undo日志版本链为:
【注意】上述的隔离级别是可重复读,但是当隔离级别改为读已提交的级别时,步骤八的结果会有所不同,不同的原因是因为不同隔离级别下生成一致性视图的时机时不一样的,之前我们说过了,可重复读的一致性视图是第一次查询语句执行的时候生成,直到事务提交;读已提交的级别的一致性视图是在每次查询语句执行前生成的,所以步骤八在读已提交的级别下一致性视图这样的
json { 'uncommitted_trx_id_list':[200], 'max_trx_id': 300 }
所以,在查询时找到的就是row_id=2,trx_id=100和row_id=3,trx_id=0的undo日志,结果为(2,Tom,18),(3,Herry,24)
id | name |
---|---|
1 | Tom |
2 | Tim |
3 | Mary |
7 | Lucy |
20 | Jemy |
id为索引字段,此时数据就会划分为以下几个区间:(3,7),(7,20)和(20,∞);
当执行insert into tabeleName (id,name) value(10,'Herry')
时,处理会给id=10添加行锁外,还所锁(7,20)范围的所有行,事务在执行id在这个范围内的所有更新操作时,事务页会处于等待锁释放,这样的锁就称为间隙锁
【拓展】间隙锁 + 行锁 组成的左左开右闭的区间锁就成为 临键锁
其实对于锁使用的优化一定程度上就是对索引的设计的优化;
try...catch...
进行补偿],可不使用事务,但是不建议使用Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。