事务的ACID特性
Atomic(原子性)
所有语句作为一个单元全部成功执行或全部取消,不能出现中间状态。
Consistent(一致性)
如果数据库在事务开始时处于一致状态,则在执行该事务期间将保留一致状态。
Isolated(隔离性)
事务之间不相互影响。
Durable(持久性)
事务成功完成后,所做的所有更改都会准确地记录在数据库中。所做的更改不会丢失。
事务的生命周期(标准的事务控制语句)
1)如何开启事务
begin;
2)标准的事务语句
DML :
insert
update
delete
3)事务的结束
提交:
commit;
回滚:
rollback;
自动提交机制(autocommit)
select @@autocommit;
在线修改参数:
1)会话级别
创建所需数据库和表并写入内容:
create database lkk default charset utf8mb4 collate utf8mb4_bin;
use lkk;
create table t1 (id int,name varchar(20));
insert into t1(id,name) values(1,'张三');
select * from t1;
临时关闭自动提交
set autocommit=0;
及时生效,只影响当前登录会话
select @@autocommit;
修改表中内容:
update t1 set name='李四' where id=1;
select * from t1;
复制当前会话:
use lkk;
select * from t1;
在第一个会话提交:
commit;
在另一个会话再次查看:
select * from t1;
回滚
修改表中内容:
update t1 set name='王五' where id=1;
select * from t1;
数据回滚:
rollback;
select * from t1;
2)全局级别
set global autocommit=0;
断开窗口重连后生效,影响到所有新开的会话
(3)永久修改(重启生效)
修改配置文件
vim /etc/my.cnf
添加:
autocommit=0
隐式提交的情况
触发隐式提交的语句:
begin
a
b
create database
导致提交的非事务语句:
DDL语句:(ALTER、CREATE 和 DROP)
DCL语句:(GRANT、REVOKE 和 SET PASSWORD)
锁定语句:(LOCK TABLES 和 UNLOCK TABLES)
导致隐式提交的语句示例:
TRUNCATE TABLE
LOAD DATA INFILE
SELECT FOR UPDATE
事务的ACID如何保证?
- 概念名词
redo log:重做日志
ib_logfile0~1:默认50M,轮询使用
redo log buffer:redo内存区域
ibd:存储数据行和索引
data buffer pool:缓冲区池,数据和索引的缓冲
LSN:日志序列号
ibd、redolog、data buffer pool、redo buffer
MySQL 每次数据库启动,都会比较磁盘数据页和redolog的LSN,必须要求两者LSN一致数据库才能正常启动
WAL (持久化):
write ahead log 日志优先写的方式实现持久化
日志是优先于数据写入磁盘的
脏页:
内存脏页,内存中发生了修改,没写入到磁盘之前,我们把内存页称之为脏页
CKPT:
Checkpoint,检查点,就是将脏页刷写到磁盘的动作
TXID:
事务号,InnoDB会为每一个事务生成一个事务号,伴随着整个事务
事务日志-- redo 重做日志
作用:
主要功能保证D,A、C也有一定得作用
记录了内存数据页的变化
提供快速的持久化功能(WAL)
CSR过程中实现前滚的操作(磁盘数据页和redo日志LSN一致)
redo日志位置
cd /usr/local/mysql/data/
redo的日志文件:
redo buffer
redo的buffer:数据页的变化信息+数据页当时的LSN号
redo的刷写策略
commit;
刷新当前事务的redo buffer到磁盘
还会顺便将一部分redo buffer中没有提交的事务日志也刷新到磁盘
MySQL在启动时,必须保证redo日志文件和数据文件LSN必须一致,如果不一致就会触发CSR,最终保证一致
情况一:
我们做了一个事务,begin;update;commit
- 在begin,会立即分配一个TXID=tx_01
- update时,会将需要修改的数据页(dp_01,LSN=101),加载到data buffer中
- DBWR线程,会进行dp_01数据页修改更新并更新LSN=102
- LOGBWR日志写线程,会将dp_01数据页的变化+LSN+TXID存储到redobuffer
- 执行commit时,LGWR日志写线程会将redobuffer信息写入redolog日志文件中,基于WAL原则,在日志完全写入磁盘后,commit命令才执行成功(会将此日志打上commit标记)
- 假如此时宕机,内存脏页没有来得及写入磁盘,内存数据全部丢失
- MySQL再次重启时,必须要redolog和磁盘数据页的LSN是一致,但是此时dp_01,TXID=tx_01磁盘是LSN=101,dp_01,TXID=tx_01,redolog中LSN=102
MySQL此时无法正常启动,MySQL触发CSR,在内存追平LSN号,触发ckpt,将内存数据页更新到磁盘,从而保证磁盘数据页和redolog LSN一值,这时MySQL正常启动
以上的工作过程,我们把它称之为基于REDO的"前滚操作"
- undo(回滚日志)
作用:
在 ACID特性中,主要保证A的特性,同时对CI也有一定功效
- 记录了数据修改之前的状态
- rollback 将内存的数据修改恢复到修改之前
- 在CSR中实现未提交数据的回滚操作
- 实现一致性快照,配合隔离级别保证MVCC,读和写的操作不会互相阻塞
- 锁(为了防止出现脏读、幻读、不可重复读)
读锁(共享锁):我读的时候,其他人不能改
写锁(排他锁):我写的时候,其他不能改,也不能读
写锁又分为:
row-level lock(行锁):锁行
gap lock(间隙锁):锁区域、锁范围
next-key lock(临键锁):行锁+间隙锁
- 隔离级别
RU(读未提交):可脏读、幻读,一般不用
RC(读已提交):可能出现幻读,可以防止脏读(高并发网站为了快速读写,会使用RC级别)
RR(可重复读):防止"幻读"现象,利用的是undo的快照技术+GAP(间隙锁)+NextLock(临键锁) (默认级别)
SR(可串行化):可以防止死锁,但是并发事务性能较差 (独占进程,排队进行各项事务)
transaction_isolation=read-uncommitted#隔离级别最低,并发性能高
transaction_isolation=read-committed#锁定正在读取的行
transaction_isolation=REPEATABLE-READ#锁定所读取的所有行
MVCC ---> undo快照
MVCC(Multiversion Concurrency Control)多版本并发控制。即通过数据行的多个版本管理来实现数据库的并发控制,使得在InnoDB事务隔离级别下执行一致性读操作有了保障。
RU会出现脏读
RC会出现不可重复读,也会出现幻读
RR通过MVCC基础解决了不可重复读,但是有可能会出现幻读现象
在RR模式下,GAP和Next-lock进行避免幻读现象,必须索引支持
查看隔离级别:
select @@tx_isolation;
脏读、幻读、不可重复读的区别:
- 脏读:是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问这个数据,然后使用了这个数据
- 不可重复读:是指在一个事务内多次读同一数据,在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读
- 幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样
不可重复读的重点是修改:
同一事务,两次读取到的数据不一样。
幻读的重点在于新增或者删除:
同样的条件,第 1 次和第 2 次读出来的记录数不一样
脏读:
强调的是第二个事务读到的不够新
验证隔离级别
创建库和表并写入数据
create database world;
use world;
create table t1 (id int primary key not null,name varchar(20));
insert into t1 values (1,'zhangsan'),(2,'lisi'),(3,'wangwu'),(4,'maliu'),(5,'zhuqi'),(6,'shenba'),(7,'gengjiu');
1)修改隔离级别为RU,验证脏读
vim /etc/my.cnf
添加:
transaction_isolation=read-uncommitted
autocommit=0
重启MySQL
systemctl restart mysqld
查看当前隔离级别:
select @@tx_isolation;
use world;
select * from t1;
复制当前会话
第一个会话:
update t1 set name='hehe' where id=1;
select * from t1;
第二个会话:
use world;
select * from t1;
能看到未提交(commit)之前的数据就是脏读(未提交就可以读到修改后的数据)
2)修改隔离级别为RC, (避免脏读)验证不可重复读
vim /etc/my.cnf
修改:
transaction_isolation=read-committed
重启MySQL
systemctl restart mysqld
第一个会话:
use world;
select * from t1;
update t1 set name='haha' where id>3;
第二个会话:
insert into t1 values (8,'zhangsan'),(9,'lisi'),(10,'wangwu');
commit;
select * from t1;
第一个会话:
commit;
select * from t1;
第二个会话先提交,发现最终查看结果不一样,幻读
3)修改隔离级别为RR,避免脏读、不可重复读、可能会导致幻读
vim /etc/my.cnf
修改:
transaction_isolation=repeatable-read
重启MySQL
systemctl restart mysqld
第一个会话:
use world;
select * from t1;
update t1 set name='lkk' where id=1;
第二个会话:
insert into world.t1 values(14,'a'),(15,'b'),(16,'c');
发现数据无法写入,然后报错提示锁等待超时。
InnoDB核心参数的介绍
- 存储引擎默认设置
default_storage_engine=innodb
- 启用独立表空间
innodb_file_per_table=1
- 共享表空间文件个数和大小
innodb_data_file_path=ibdata1:512M:autoextend
Innodb_flush_method=(O_DIRECT, fsync)
作用: 控制的是 Redo buffer 和 buffer pool
fsync :
O_DIRECT :
O_DSYNC :
- 最高安全模式
innodb_flush_log_at_trx_commit=1
innodb_flush_method=O_DIRECT
常见的innodb_flush_method取值有以下几种:
fdatasync模式:写数据时,write这一步并不需要真正写到磁盘才算完成(可能写入到操作系统buffer中就会返回完成),真正完成是flush操作,buffer交给操作系统去flush,并且文件的元数据信息也都需要更新到磁盘。
O_DSYNC模式:写日志操作是在write这步完成,而数据文件的写入是在flush这步通过fsync完成
O_DIRECT模式:数据文件的写入操作是直接从mysql innodb buffer到磁盘的,并不用通过操作系统的缓冲,而真正的完成也是在flush这步,日志还是要经过OS缓冲
- 最高性能
innodb_flush_log_at_trx_commit=0
innodb_flush_method=fsync
redo日志设置有关的
show variables like "innodb_log_buffer_size"; ——查看当前日志缓冲区大小
vim /etc/my.cnf
添加:
innodb_log_buffer_size=16777216 ——内存日志缓冲区大小
innodb_log_file_size=50331648 ——磁盘日志文件大小(重做日志)
innodb_log_files_in_group = 2 ——磁盘日志文件文件个数(重做日志)
innodb_buffer_pool_size ——设置缓冲池的大小
innodb_buffer_pool_instances ——设置缓冲池的数量
脏页刷写策略
innodb_max_dirty_pages_pct=25 (25%)
还有哪些机制会触发写磁盘?(从内存写入磁盘)
CSR (比LSN,前滚)
redo满了
标签:事务,buffer,t1,MySQL,磁盘,日志,select From: https://blog.csdn.net/2402_88627342/article/details/145055757