MySQL存储引擎(InnoDB)
存储引擎介绍
类似于Linux系统中的文件系统
功能
1 | 数据读写 |
种类
1 | InnoDB |
InnoDB存储引擎介绍
在MySQL5.5版本之后,默认的存储引擎,提供高可用性和高性能
优点
1 | 1、事务(Transaction) |
存储引擎查看
使用select确认会话存储引擎
1
2select @@default_storage_engine;
show variables like '%engine%';默认存储引擎设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14# 不会在生产中操作
会话级别
set default_storage_engine=myisam;
全局级别(仅影响新会话)
set global default_storage_engine=myisam;
重启之后,所有参数均失效
# 放入配置文件永久生效
vim /etc/my.cnf
[mysqld]
default_storage_engine=myisam
存储引擎是表级别的,每个表创建时可以指定不同的存储引擎,但是建议统一为Innodb查看表的存储引擎
1
2
3
4
5
6
7
8# 查看单表的存储引擎
show create table world.city\G
use world;
show table status like 'countrylanguage'\G
# 查看每个表的存储引擎
select table_schema,table_name ,engine from information_schema.tables where table_schema not in ('sys','mysql','information_schema','performance_schema');修改一个表的存储引擎(碎片整理)
1
2
3
4
5
6
7
8alter table world.city engine innodb;
# 这条命令还可以进行innodb表的碎片化整理
# 将test数据库下的所有1000表,存储引擎从MyISAM替换为innodb
select concat("alter table ",table_name," engine innodb;")
from information_schema.tables
where table_schema='test'
into outfile '/tmp/alter.sql';
InnoDB物理存储结构
最直观的存储方式
1 | # 数据目录 /data/mysql/data |
表空间
1 | 需要将所有数据存储到同一个表空间中 ,管理比较混乱 |
共享表空间
1 | # 共享表空间 ibdata1 |
独立表空间
1 | 从5.6,默认表空间不再使用共享表空间,替换为独立表空间。 |
迁移表空间
1 | # 迁移表空间功能,导入和导出表空间 |
事务
事务ACID
特性
影响DML语句(
insert
、update
、delete
和一部分select
)
Atomic
原子性
所有语句作为一个单元全部成功执行或全部取消,不能出现中间状态
Consistent
一致性
如果数据库在事务开始时处于一致状态,则在执行该事务期间将保留一致状态
Isolated
隔离性
事务之间不相互影响
Durable
持久性
事务成功完成后,所做的所有更改都会准确地记录在数据库中,所做的更改不会丢失。
事务的生命周期
事务的开始
1
2
3begin;
start transaction;
# 在5.5 以上的版本,不需要手工begin,只要你执行的是一个DML,会自动在前面加一个begin命令。事务的结束
1
2
3
4
5
6commit;
完成一个事务,一旦事务提交成功,,就说明具备ACID特性了
rollback;
回滚事务
将内存中已执行过的操作回滚回去例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30# mysql 窗口一
user world;
begin;
delete from city where id > 3000;
delete from city where id > 2000;
# mysql 窗口二
use world;
select * from city; # 此时查询的数据还是4079条数据,并没有被删除
begin;
delete from city where id > 1800;
# 此时会卡主,因为在等上一个事务,隔离性
# 操作相同的语句会等上一个事务结束之后才会继续
# 过了超时时间就会报 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
# mysql 窗口一
commit; # 此时提交
# mysql 窗口二
delete from city where id > 1800; # 这个窗口就可以执行操作了
commit;
# 回滚事务
begin;
delete from city where id=100;
select * from city where id=100;
rollback; # 对上面的sql操作语句进行回滚,如果commit了,就不能回滚
select * from city where id=100;
commit;自动提交策略(
autocommit
)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17默认执行DML语句的时候会自动的在语句前加 begin 和 commit,针对需要使用事务功能的语句 begin 或者直接全部使用事务功能
select @@autocommit; # 默认开启的 1 为开启
set autocommit=0; # 关闭自动提交策略
set global autocommit=0; # 全局关闭自动提交策略
# 永久关闭自动提交策略
vim /etc/my.cnf
[mysqld]
...
autocommit=0
...
自动提交是否打开,一般在有事务需求的MySQL中,将其关闭
不管有没有事务需求,我们一般也都建议设置为0,可以很大程度上提高数据库性能事务的隐式控制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27# 隐式回滚
1. 关闭窗口的时候会自动回滚
# 已经永久关闭了自动提交策略
2. 删除会话ID
show processlist;
kill 3;
# 隐式提交
begin;
a
b
begin; # 此时上面的事务自动提交了
begin;
a
b
set autocommit=1; # 此时上面的事务也自动提交了
# 导致提交的非事务语句
DDL语句: (ALTER、CREATE 和 DROP)
DCL语句: (GRANT、REVOKE 和 SET PASSWORD)
锁定语句: (LOCK TABLES 和 UNLOCK TABLES)
导致隐式提交的语句示例:
TRUNCATE TABLE
LOAD DATA INFILE
SELECT FOR UPDATE
InnoDB事务的ACID是如何保证的
基础概念
1
2
3
4
5
6
7
8
9
10
11
12
13redo log ---> 重做日志 ib_logfile0~1 50M ,轮询使用
redo log buffer ---> redo内存区域
t1.ibd ---> 存储 数据行和索引
buffer pool ---> 数据缓冲区池,数据和索引的缓冲
LSN : 日志序列号
磁盘数据页,redo文件,buffer pool,redo buffer
MySQL 每次数据库启动,都会比较磁盘数据页和redolog的LSN,必须要求两者LSN一致数据库才能正常启动
WAL : write ahead log 日志优先写的方式实现持久化
脏页: 内存脏页,内存中发生了修改,没写入到磁盘之前,我们把内存页称之为脏页
CKPT: Checkpoint,检查点,就是将脏页刷写到磁盘的动作
TXID: 事务号,InnoDB会为每一个事务生成一个事务号,伴随着整个事务redo log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# Redo是什么
redo,顾名思义“重做日志”,是事务日志的一种。
# 作用是什么
在事务ACID过程中,实现的是“D”持久化的作用。对于AC也有相应的作用
# redo日志位置
redo的日志文件:iblogfile0 iblogfile1
# redo buffer
redo的buffer:数据页的变化信息+数据页当时的LSN号
LSN:日志序列号 磁盘数据页、内存数据页、redo buffer、redolog
# redo的刷新策略
commit;
刷新当前事务的redo buffer到磁盘
还会顺便将一部分redo buffer中没有提交的事务日志也刷新到磁盘CSR前滚
1
2
3
4
5
6
7
8
9
10
11
12
13MySQL : 在启动时,必须保证redo日志文件和数据文件LSN必须一致, 如果不一致就会触发CSR,最终保证一致
情况一:
我们做了一个事务,begin;update;commit.
1.在begin ,会立即分配一个TXID=tx_01.
2.update时,会将需要修改的数据页(dp_01,LSN=101),加载到data buffer中
3.DBWR线程,会进行dp_01数据页修改更新,并更新LSN=102
4.LOGBWR日志写线程,会将dp_01数据页的变化+LSN+TXID存储到redobuffer
5. 执行commit时,LGWR日志写线程会将redobuffer信息写入redolog日志文件中,基于WAL原则,
在日志完全写入磁盘后,commit命令才执行成功,(会将此日志打上commit标记)
6.假如此时宕机,内存脏页没有来得及写入磁盘,内存数据全部丢失
7.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
回滚日志1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# undo是什么
undo,顾名思义“回滚日志”
# 作用是什么
在事务ACID过程中,实现的是“A” 原子性的作用
另外CI也依赖于Undo
在rolback时,将数据恢复到修改之前的状态
在CSR实现的是,将redo当中记录的未提交的时候进行回滚.
undo提供快照技术,保存事务修改之前的数据状态.保证了MVCC,隔离性,mysqldump的热备
# 概念性的东西
redo怎么应用的
undo怎么应用的
CSR(自动故障恢复)过程
LSN :日志序列号
TXID:事务ID
CKPT(Checkpoint)锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24# 锁介绍
锁顾名思义就是锁定的意思,提供的是隔离的方面的功能,需要配合 undo+隔离级别一起来实现
# InnoDB锁级别
行级锁,修改这一行就会持有这行的锁,默认情况是排他锁(悲观锁)
悲观锁:行级锁定(行锁)
谁先操作某个数据行,就会持有<这行>的(X)锁.
乐观锁: 没有锁
# 死锁
# mysql窗口一
begin;
update city set countrycode='CHN' where id=1;
update city set countrycode='CHN' where id=2;
# mysql窗口二
begin;
update city set countrycode='USA' where id=2;
update city set countrycode='USA' where id=1;
# 业务逻辑有问题,开发中不能出现
# ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
# 锁的作用
在事务ACID过程中,“锁”和“隔离级别”一起来实现“I”隔离性和"C" 一致性 (redo也有参与)事务的隔离级别
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65作用: 影响到数据的读取
默认的级别是 RR模式
transaction_isolation 隔离级别(参数)
# 查看隔离级别
select @@tx_isolation;
show variables like '%iso%';
# RR模式
# 创建表环境
use world;
begin;
create table t1 (id int not null , ticker int null);
desc t1;
insert into t1 values (1,1);
commit;
select * from t1;
# mysql窗口一
use world;
begin;
select * from t1; # 这一步窗口二也开始查询
update t1 set ticker=0 where id=1;
select * from t1;
commit;
# mysql窗口二
use world;
select * from t1; # 和窗口一的第一次查询时间同步
select * from t1; # 当窗口一已经提交了事务之后再查询发现并没有改变
# 这个现象就是可重复读现象,如果想在窗口二看到改变后的情况,先commit一下
# 修改隔离级别
set global transaction_isolation='read-committed'; # 此时是RC模式 需要退出窗口后重新进入生效
# 测试,设置之后重新打开mysql窗口
# mysql窗口一
select @@tx_isolation; # 查看隔离级别
use world;
begin;
select * from t1; # # 这一步窗口二也开始查询
update t1 set ticker=1 where id=1;
select * from t1;
commit;
select * from t1;
# mysql窗口二
selet @@tx_isolation; # 查看隔离级别
use world;
select * from t1; # 和窗口一的第一次查询时间同步
select * from t1; # 当窗口一提交了事务之后再查询发现此时已经改变了
隔离级别负责的是,MVCC,读一致性问题
RU : 读未提交,可脏读,一般部议叙出现
RC : 读已提交,可能出现幻读,可以防止脏读.
RR : 解决了不可重复读,功能是防止"幻读"现象 ,利用的是undo的快照技术+GAP(间隙锁)+NextLock(下键锁)
SR : 可串行化,可以防止死锁,但是并发事务性能较差
幻读现象是由MVCC+GAP+Next-Lock解决
补充: 在RC级别下,可以减轻GAP+NextLock锁的问题,但是会出现幻读现象,一般在为了读一致性会在正常select后添加for update语句.但是,请记住执行完一定要commit 否则容易出现所等待比较严重.
例如:
[world]>select * from city where id=999 for update;
[world]>commit;幻读
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49# 环境准备
select @@tx_isolation; # RC模式
select @@autocommit; # 0
# 建库建表
create database test charset utf8mb4;
use test;
create table t1(id int not null primary key auto_increment, num int not null);
insert into t1(num) values (1),(3),(5);
commit;
# mysql 窗口一
begin;
update t1 set num=10 where num>=3;
commit;
# 上面的更新语句和窗口二的插入语句两个事务同时进行
select * from t1; # 此时发现会多一条 num 为 7 的列,这种现象就是幻读
# mysql 窗口二
begin;
insert into t1(num) values(7);
commit;
# RR 模式下
# 环境准备
set global transaction_isolation='repeatable-read'; # 需要退出窗口后重新进入
select @@tx_isolation; # RR级别
select @@autocommit; # 0
# 创建表
use test;
create table t2(id int not null primary key auto_increment, num int not null);
insert into t2(num) values (1),(3),(5);
alter table t2 add index idx(num);
commit;
# mysql 窗口一
begin;
update t2 set num=10 where num>=3; # 更新表之后开始窗口二
# mysql 窗口二
begin;
insert into t1(num) values(7);
# 当窗口一更新之后此时再插入发现夯住
# ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
# 防止幻读
# 辅助索引+Next-Lock幻读
不可幻读
InnoDB核心参数
双一标准之一(innodb_flush_log_at_trx_commit
)
1 | # 参数 |
Innodb_flush_method
1 | # 作用 |
redo
日志参数设置
1 | innodb_log_buffer_size=16777216 # 调大并发的数量会越多,128M起,结合业务调整,这边单位是字节 |
innodb_buffer_pool_size
1 | # 一般调整为物理内存的50%-80%(系统中只有一个mysql实例) |