第六天
mysql索引
mysql索引类型
- btree
- rtree
- hash
- fulltext
- gis
btree种类
- b-tree
- b+tree
- btree*:增强的b+tree
mysql b+tree索引构建过程
-
聚簇索引btree结构(innodb独有)
区===簇
-
- 建表时,指定主键列,mysql innodb会将主键作为聚簇索引列,比如id not null primary key
- 没有指定主键,自动选择唯一键(unique)的列,作为聚簇索引
- 以上都没有,生成隐藏聚簇索引
作用:有了聚簇索引之后,将来插入的数据行,在同一个区内,都会按照id值的顺序,有序在磁盘存储数据。
-
辅助索引btree结构
使用普通列作为条件构建的索引。需要人为创建
优化非聚簇索引之外的查询条件的优化
==alter table table_name add index idx(column_name);==
-
分类
单列索引
-
联合索引(多列)
==alter table table_name add index idx(column_name1,column_name2);==
使用多列组合一个索引,但是要最左原则
-
- 查询条件中,必须要包含最左列
- 建立联合索引时,一定要选择重复值少的列,作为最左列
-
前缀索引
- 如果我们所选择的索引值长度多长,会导致索引树高度增高,会导致索引应用时,需要读取更多的索引数据页,所以可以选择大字段的前面部分字符作为索引生成条件
- ==alter table table_name add index idx_d(column_name(5));==
mysql中建议索引树高度==3-4层==
b+tree索引树高度影响因素
- 索引字段较长:解决用前缀索引
- 数据行过多:解决分区表,归档表(pt-archive),分布式架构(大企业)
- 数据类型:选择合适的数据类型
索引的管理命令
-
什么时候创建索引
按照业务语句的需求创建合适的索引,并不是将所有列都建立索引,不是索引越多越好
将索引建立在,经常where ,group by ,order by, join on的条件
-
- 如果冗余索引过多,表的数据行变化的时候,很有可能会导致索引频繁更新。会阻塞很多正常的业务更新的请求
- 索引过多,会导致优化器选择出现偏差
管理命令
-
查询表的索引情况
==desc table_name;==
-
key:
- pri 聚簇索引
- mul辅助
- uni唯一索引
==
show index from table_name;
==
-
建立索引
- ==alter table table_name add index idx(column_name);==
- idx可以子自己定义(索引名)
- ==alter table table_name add index idx(column_name);==
-
删除索引
- ==alter table table_name drop index index_name;==
==知识回答==
数据库中的表长成什么样
- mysql用来存储数据行的逻辑结构,表的数据行最终存储到了很多的page
- innodb存储引擎,会按照聚簇索引,有序的组织存储表数据到各个区的连续页上
- 这些连续的数据页,成为了聚舱索引的叶子节点。你可以认为聚簇索引就是原表数据
- 回表即是,回聚簇索引
什么是回表
- 辅助索引:将辅助索引列值+id主键值,构建辅助索引b树结构
- 用户使用,辅助索引列作为条件查询时,首先扫描辅助索引的b树
- 如果辅助索引能够完全覆盖我们的查询结果时,就不需要回表
- 如果不能完全覆盖,只能通过得出的id主键值,回到聚簇索引(回表扫描,最终)得到想要的结果
回表会带来什么影响
- io量级变大
- iops会增大
- 随机io会增大
怎么减少回表
- 将查询尽可能用id主键查询
- 设计合理的辅助索引(联合索引)
- 更精确的查询条件+联合索引
- 优化器的算法:MRR---???
更新数据时,会对索引有影响吗,数据的变化会使索引实时更新吗
- 对于聚簇索引会立即更新
- 对于辅助索引,不是实时更新的
- 在innodb内存结果中,加入insert buffer(会话级别),现在版本叫change。change buffer功能是临时缓冲辅助索引需要的数据更新
- 当我们需要查询新insert的数据,会在内存中进行merge(合并)操作,此时辅助索引就是最新的
压力测试
### 导入测试表
mysql> source t100w.sql
### 压测命令(用户名为root,密码123,可以自己修改)
mysqlslap --defaults-file=/etc/my.cnf \
--concurrency=100 --iterations=1 --create-schema='test' \
--query="select * from test.t100w where k2='FGCD'" engine=innodb \
--number-of-queries=2000 -uroot -p123 -verbose
第七天
执行计划
==查看执行计划==
- ==
explain
sql语句== - ==
desc
sql语句==
执行计划显示结果的分析
- table:此次查询涉及到的表
- ==type:查询类型==
- 全表扫:不用任何的缩影。ALL
- ==索引扫:index
- index:全索引扫
- range:索引范围查询
- ref:辅助索引等值查询
- eq_ref:多表链接中,非驱动表链接条件是主键或唯一键
- const:聚簇索引等值查询
长度
(==列的最大储值字节长度==)
- ==联合索引覆盖长度==
- 此次查询,可以帮助我们判断,走了联合索引的几部分
- using filesort
- 表示此次查询使用到了文件排序
- 说明在查询中的排序操作:order by ,group by ,distinct
==建索引原则==(一些注意)
- 必须要有主键,如果没有可以做为主键条件的列,创建无关列
- 经常做为where条件列 order by group by join on, distinct 的条件(业务:产品功能+用户行为)
- 最好使用唯一值多的列作为索引,如果索引列重复值较多,可以考虑使用联合索引
- 列值长度较长的索引列,我们建议使用前缀索引.
- 降低索引条目,一方面不要创建没用索引,不常使用的索引清理,percona toolkit( xxxxx)
- 索引维护要避开业务繁忙期
一些注意:
查询条件使用函数在索引列上,或者对索引列进行运算,运算包括(+,-,,/,! 等),就会导致索引失效*
隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误.
<> (不等于!),not in 不走索引(针对的是辅助索引)
单独的>,<,in 有可能走,也有可能不走,和结果集有关,尽量结合业务添加limit
or或in 尽量改成union(条件:要重复值不多的列)
like "%_" 百分号在最前面不走索引
- %linux%类的搜索需求,可以使用elasticsearch+mongodb 专门做搜索服务的数据库产品
查询结果集是原表中的大部分数据,应该是25%以上。
查询的结果集,超过了总数行数25%,优化器觉得就没有必要走索引了。
假如:tab表 id,name id:1-100w ,id列有(辅助)索引
select * from tab where id>500000;
如果业务允许,可以使用limit控制。
怎么改写 ?
结合业务判断,有没有更好的方式。
如果没有更好的改写方案
尽量不要在mysql存放这个数据了。放到redis里面。
索引本身失效,统计数据不真实
索引有自我维护的能力。
对于表内容变化比较频繁的情况下,统计信息不准确,过旧。有可能会出现索引失效。
一般是删除重建现象:
有一条select语句平常查询时很快,突然有一天很慢,会是什么原因
select? --->统计数据不真实,导致索引失效。
DML ? --->锁冲突
统计数据不真实
mysql库中的两个表:
- innodb_index_stats
- innodb_table_stats
解决方法:
- optimize table table_name;
- alter table table_name engine=innodb;
- 删除重建
==优化器针对索引的算法==
-
==mysql索引的自优化-AHI(自适应hash索引)==
- mysql的innodb引擎,只能创建btree
- ==AHI的作用:==
- 自动评估热的内存索引page,生成hash索引表
- 帮助innodb快速读取索引页,加快索引读取的速度
- ==相当于索引的索引==
-
==mysql索引的自优化-change buffer==
- 对于聚簇索引会立即更新
- 对于辅助索引,不是实时更新的
- 在innodb内存结果中,加入insert buffer(==会话级别==),现在版本叫change buffer。change buffer功能是临时缓冲辅助索引需要的数据更新
- 当我们需要查询新insert的数据,会在内存中进行merge(合并)操作,此时辅助索引就是最新的
-
ICP(索引下推)
-
解决了联合索引只能部分应用情况
为了减少不必要的数据页被扫描,将不走索引的条件,在engine层取数据之前先做二次过滤,一些无关数据就会被提前过滤掉
- https://dev.mysql.com/doc/refman/5.7/en/index-condition-pushdown-optimization.html
-
-
MRR
- ==set global optimizer_switch='mrr=on,mrr_cost_based=off';==
- 辅助索引---》==sort id==---》回表---》聚簇索引
-
NLJ---多表联合查询
https://dev.mysql.com/doc/refman/5.7/en/nested-loop-joins.html
SNLJ
-
BNLJ
-
在多表关联条件匹配时,不再一次一次进行循环。
而是采用一次性将驱动表的关联值和非驱动表匹配,一次性返回结果。
主要优化了,cpu消耗,减少io次数
-
-
BKA---多表联合查询
https://dev.mysql.com/doc/refman/5.7/en/bnl-bka-optimization.html
==set global optimizer_switch='mrr=on,mrr_cost_based=off';==
==set global optimizer_switch='batched_key_access=on';==
-
主要作用:使用来优化非驱动表的关联列有辅助索引
相当于bnl+mrr的功能
优化器算法
==show variable like '%switch%';==
==select @@optimizer_switch;==
默认的优化器算法如下:
- index_condition_pushdown=on---ICP
- mrr=on
- mrr_cost_based=on
- block_netsted_loop=on
- batched_key_access=off
如何修改?
==my.cnf==
==set global optimizer_switch='batched_key_access=on';==
hints
SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1 FROM t3 WHERE f1 > 30 AND f1 < 33; SELECT /*+ BKA(t1) NO_BKA(t2) */ * FROM t1 INNER JOIN t2 WHERE ...; SELECT /*+ NO_ICP(t1, t2) */ * FROM t1 INNER JOIN t2 WHERE ...; SELECT /*+ SEMIJOIN(FIRSTMATCH, LOOSESCAN) */ * FROM t1 ...; EXPLAIN SELECT /*+ NO_ICP(t1) */ * FROM t1 WHERE ...; SELECT /*+ MERGE(dt) */ * FROM (SELECT * FROM t1) AS dt; INSERT /*+ SET_VAR(foreign_key_checks=OFF) */ INTO t2 VALUES(2);
https://mariadb.com/kb/en/multi-range-read-optimization/
第八天
mysql存储引擎
种类
- oracle mysql
- 可以对不同的表,设定不同的存储引擎
- ==show engines;==
- 默认存储引擎:innodb(5.5版本以后)
- innodb
- myisam
- csv
- memory
- percona
- 默认存储引擎:xtradb
- mariadb
- 默认存储引擎:innodb
其他引擎(percona、mariadb)
- tokudb
- 适合于,业务中有大量插入或者删除操作的场景
- 应用于,数据量较大的业务
- myrocks
环境: zabbix 3.2 mariaDB 5.5 centos 7.3
现象 : zabbix卡的要死 , 每隔3-4个月,都要重新搭建一遍zabbix,存储空间经常爆满.
问题 :
- zabbix 版本
- 数据库版本
- zabbix数据库500G,存在一个文件里ibdata1,手动删除1个月之前的数据,空间不释放
优化建议:
- 数据库版本升级到5.7(percona)(mariadb 10.1)版本,zabbix升级更高版本
- 存储引擎改为tokudb
- 监控数据按月份进行切割(二次开发:zabbix 数据保留机制功能重写,数据库分表
- 关闭binlog和双1
- 参数调整....
优化结果:
监控状态良好
为什么?
- 原生态支持TokuDB,另外经过测试环境,5.7要比5.5 版本性能 高 2-3倍
- TokuDB:insert数据比Innodb快的多,数据压缩比要Innodb高
- 监控数据按月份进行切割,为了能够truncate每个分区表,立即释放空间
- 关闭binlog ----->减少无关日志的记录.
- 参数调整...----->安全性参数关闭,提高性能.
https://www.jianshu.com/p/898d2e4bd3a7
https://mariadb.com/kb/en/installing-tokudb/
https://www.percona.com/doc/percona-server/5.7/tokudb/tokudb_installation.html
innodb核心特性
介绍
MVCC:多版本并发控制
多缓冲区池
事务
行级锁(粒度)
外键更多复制特性
支持热备
自动故障恢复
clustered index:聚簇索引
change buffer
自适应hash索引---AHI
InnoDB和MyISAM存储引擎的替换
环境: centos 5.8 ,MySQL 5.0版本,MyISAM存储引擎,网站业务(LNMP),数据量50G左右
现象问题: 业务压力大的时候,非常卡;经历过宕机,会有部分数据丢失.
问题分析:
1.MyISAM存储引擎表级锁,在高并发时,会有很高锁等待
2.MyISAM存储引擎不支持事务,在断电时,会有可能丢失数据
职责
1.监控锁的情况:有很多的表锁等待
2.存储引擎查看:所有表默认是MyISAM
解决方案:
1.升级MySQL 5.6.10版本2.迁移所有表到新环境
3.开启双1安全参数
以上是小项目
存储引擎的管理命令
-
使用select确认
会话
存储引擎:- ==select @@default_storage_engine;==
-
修改存储引擎
-
会话级别
- set default_storage_engine=innodb;
-
全局级别
- set global default_storage_engine=innodb;
-
永久生效
-
写入配置文件
vim /etc/my.cnf
[mysqld]
default_storage_engine=innodb
-
-
-
确认每个表的存储引擎
- ==show create table table_name;==
- ==show table status like 'table_name';==
-
INFORMATION_SCHEMA 确认每个表的存储引擎
- ==select table_schema,table_name ,engine from information_schema.tables where table_schema not in ('sys','mysql','information_schema','performance_schema');==
-
修改一个表的存储引擎
- ==alter table table_name engine=innodb;==
==注意:==此命令我们经常使用他,进行innodb表的碎片整理
知识问答2
1.平常处理过的MySQL问题--碎片处理
环境:centos7.4,MySQL 5.7.20,InnoDB存储引擎
业务特点:数据量级较大,经常需要按月删除历史数据.
问题:磁盘空间占用很大,不释放
处理方法:
以前:将数据逻辑导出,手工drop表,然后导入进去
现在:
对表进行按月进行分表(partition,中间件),或者归档表(pt-archive)
业务替换为truncate方式
2.2亿行的表,想要删除其中1000w,你们公司都怎么做的?假如按照时间列条件
- 如果2亿行数据表,还没有生成,建议在设计表时,采用分区表的方式(按月range),然后删除truncate
- 如果2亿行数据表,已经存在,建议使用==pt-archive工具进行归档表==,并且删除无用数据
==3.如何批量修改==
### 需求1:将zabbix库中的所有表,innodb替换为tokudb select concat("alter table zabbix.",table_name," engine tokudb;") from information_schema.tables where table_schema='zabbix' into outfile '/tmp/tokudb.sql';
### 需求2:将所有非innodb业务表查询出来,并修改为innodb select concat('alter table ',table_schema,'.',table_name,' engine=innodb;') from information_schema.tables where engine!='InnoDB' and table_schema not in ('sys','performance_schema','information_schema','mysql') into outfile '/tmp/alter.sql';
宏观结构
-
myisam
- table_name.frm:数据字典信息(列的定义和属性)
- table_name.myd:数据行
- table_name.myi:索引
-
innodb
-
用户(独立)表空间:table_name.ibd
- 数据行,索引
-
用户的数据字典信息:table_name.frm
- 数据字典信息(列的定义和属性)
-
系统(共享)的表空间:ibdata1
包含了所有用户的数据字典信息
undo(事务回滚日志)
double write磁盘区域
change buffer磁盘区域
-
5.5:ibdata1中还会存储临时表空间+用户数据(数据行+索引)
5.6:还有临时表空间
5.7:取消了临时表空间
8.0:取消了存储的数据字典信息、undo独立了、double write磁盘区域
-
事务日志文件:ib_logfile0,ib_logfile1,.......,ib_logfileN
- 事务重做日志
-
临时表空间:ibtmp1
- 排序,分组,多表连接,子查询,逻辑备份等
-
ib_buffer_pool
- 正常关库的时候,存储缓冲区的热数据
-
[站外图片上传中...(image-4f1c4d-1612244203500)]
-
-
微观结构
表空间
为了解决存储空间扩展(类比lvm)的问题,5.5版本引入了共享表空间模式
-
mysql表空间类型
-
the system tablespace:共享表空间
- 5.5版本引入共享表空间(ibdata1),作为默认存储方式
- 用来存储:系统数据、日志、undo、临时表、用户数据和索引
- 5.5版本引入共享表空间(ibdata1),作为默认存储方式
-
file-per-table tablespaces:独立表空间
- 5.6版本默认独立表空间模式。单表单表空间
general tablespaces:普通表空间
-
undo tablespaces:undo表空间
- 存储undo logs(回滚日志)
-
the temporary tablespace:临时表空间
- 5.7默认独立
-
-
表空间管理
- 用户数据默认的存储方式,独立表空间模式。独立表空间和共享表空间可以互相切换
- 查看默认表空间模式和切换
- ==select @@innodb_file_per_table;==
- ==set global innodb_file_per_table=on;==
- /etc/my.cnf
如何扩展共享表空间大小和个数?
select @@innodb_data_file_path; ### 1.初始化之前,需要在/etc/my.cnf加入以下配置即可: innodb_data_file_path=ibdata1:1G;ibdata2:1G:autoextend ### 2.已运行的数据库上扩展多个ibdata文件 innodb_data_file_path=ibdata1:1G;ibdata2:1G;ibdata3:1G:autoextend ### 查看mysql 的err log cat /xxx/`hostname`.err |grep -i error
事务日志(redo log重做日志)
- 文件:ib_logfile0~ib_logfileN
- 控制参数:(show variables like '%innodb_log';)
- ==innodb_log_file_size=50331648==
- ==innodb_log_files_in_group=2==
- ==innodb_log_group_home_dir=./==
- 功能
- 用来存储,mysql在做修改类(DML)操作时的==数据页变化过程(变化日志)及版本号(LSN)==,属于==物理日志==
- 默认两个文件存储redo,是循环覆盖使用
undo logs(回滚日志)
- 文件:ibdataN,ibtmp1
- 控制参数:(show variables like '%undo%';)
- innodb_undo_directory=./
- show variables like '%segments%';
- ==innodb_rollback_segments=128==
- segments这个是段,回滚段有128个,其中96个在ibdata中,剩下在ibtmp1中
- 功能
- 用来存储回滚日志,可以理解为记录了每次操作的反操作,属于==逻辑日志==
- 使用快照功能,提供innodb多版本读写
- 通过记录的反操作,提供回滚功能
==内存==
- 数据内存区域(
缓冲区
)-
共享
内存区域- ==buffer pool:缓冲区池==
- ==select @@innodb_buffer_pool_size;==
- 缓冲数据页+索引页
- 会话内存区域(独有,独占)
- join_buffer_size
- key_buffer_size
- read_buffer_size
- read_rnd_buffer_size
- sort_buffer_size
-
- ==日志(log buffer)==---
共享
- ==innodb_log_buffer_size=16777216==
- 功能:负责redo日志的缓冲
==mysql总共使用内存=共享内存+会话内存*会话个数+额外的内存使用(文件系统缓存)==
==故障案列==
背景:
硬件及软件环境:
1.联想服务器(IBM) 磁盘500G 没有raid centos 6.8
2.mysql 5.6.33 innodb引擎 独立表空间
3.备份没有,日志也没开4.开发用户专用库:jira(bug追踪) 、 confluence(内部知识库) ------>LNMT
故障描述:
断电了,启动完成后“/” 只读
fsck 重启,系统成功启动,mysql启动不了。
结果:confulence库在 , jira库不见了办法(==表空间迁移==):
### 表空间迁移: create table xxx alter table confulence.t1 discard tablespace; alter table confulence.t1 import tablespace; ### 虚拟机测试可行。
处理问题思路
### confulence库中一共有107张表。 1、创建107和和原来一模一样的表。 他有2016年的历史库,我让他去他同时电脑上 mysqldump备份confulence库 mysqldump -uroot -ppassw0rd -B confulence --no-data >test.sql 拿到你的测试库,进行恢复 到这步为止,表结构有了。 2、表空间删除。 select concat('alter table ',table_schema,'.'table_name,' discard tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/discad.sql'; source /tmp/discard.sql 执行过程中发现,有20-30个表无法成功。主外键关系 很绝望,一个表一个表分析表结构,很痛苦。 set foreign_key_checks=0 跳过外键检查。 把有问题的表表空间也删掉了。 3、拷贝生产中confulence库下的所有表的ibd文件拷贝到准备好的环境中 select concat('alter table ',table_schema,'.'table_name,' import tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/import.sql'; source /tmp/import.sql 4、验证数据 表都可以访问了,数据挽回到了出现问题时刻的状态
第九天
事务
什么是事务
事务是伴随着交易类的业务场景出现的工作机制。保证交易的‘和谐’
事务ACID的特性
- atomicity:原子性
- 在一个事务工作单元中,所有标准事务语句(DML),要么全成功,要么全回滚
- consistency:一致性
- 事务发生前,中,后都应该保证数据始终一致状态
- 所有的特性,都是最终保证一致性
- isolation:隔离性
- mysql可以支持多事务并发工作的系统
- 在某个事务工作的时候,不能受到其他事务的影响
- durability:持久性
- 当事务提交(commit命令执行成功后),此次事务操作的所有数据“落盘”,都要永久保存下去
- 不会因为数据实例发生故障,导致数据失效
事务生命周期管理
-
标准事务控制语句
- ==begin/start transaction;==:开启事务
- ==commit;==:提交事务
- ==rollback;==:回滚事务
-
标准的事务语句(DML)
- insert
- update
- delete
select
-
自动提交功能
==select @@autocommit;==
==set autocommit=0;==
==set global autocommit=0;==
### vi /etc/my.cnf autocommit=0
autocommit=1,一般适合于非交易类的业务场景
-
隐式事务控制
-
隐式提交
设置了autocommit=1
==DDL,DCL等非DML语句时,会触发隐式提交==
-
用于隐式提交的 SQL 语句:
begin
a
b
begin
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如何保证(概念)==
- 重做日志
- redo log
- 重做日志 ib_logfile0~1 48M , 轮询使用
- 记录的是数据页的变化
- redo log buffer :redo内存区域
- redo log
- 数据页存储位置
- ibd :存储 数据行和索引
- buffer pool :缓冲区池, 数据页和索引的缓冲
- ==LSN : 日志序列号==
- 磁盘数据页, redo文件, buffer pool, redo buffer
- ==MySQL 每次数据库启动, 都会比较磁盘数据页和redo log的LSN, 必须要求两者LSN一致数据库才能正常启动==
- ==WAL : write ahead log 日志优先写的方式实现持久化==
- 脏页: 内存脏页, 内存中发生了修改, 没回写入到磁盘之前, 我们把内存页称之为脏页.
- CKPT:Checkpoint, 检查点, 就是将脏页刷写到磁盘的动作
- TXID: 事务号, InnoDB会为每一个事务生成一个事务号, 伴随着整个事务生命周期.
- undo:ibdata1,存储了事务工作过程中的回滚信息
==innodb事务的工作流程==
-
redo log
redo,顾名思义“重做日志”,是事务日志的一种
-
作用
- 在事务ACID过程中,实现的是D持久化的作用。对于AC也有相应的作用
-
日志位置
- ib_logfile0~N
-
==redo buffer==
- 数据页的变化信息(变化日志)+数据页当时的LSN号
- LSN:日志序列号 磁盘数据页、内存数据页、redo buffer、redolog
-
redo的刷新策略
- commit
- 刷新当前事务的redo buffer 到磁盘
- 还会顺便将一部分redo buffer中没有提交的事务日志也刷新到磁盘
-
==补充(CSR)==
-
redo 存储的是在事务工作流程中,数据页变化
commit时会立即写入磁盘(默认),日志落盘成功commit才成功。
正常mysql工作过程中,主要的工作室提供快速D的功能
mysql出现crash异常宕机时,主要提供的是前滚功能(CSR)
-
-
==select @@innodb_flush_log_at_trx_commit;==
-
innodb_flush_log_at_trx_commit=0/1/2
1:在每次事务提交时,会立即刷新redo buffer到磁盘,commit才能成功(默认为1)
0:每秒刷新redo buffer到os cache,再fsync()到磁盘,异常宕机时,会有可能导致丢失1s内的事务
2:每次事务提交,都立即刷新redo buffer到os cache,再每秒fsync()磁盘,异常宕机时,会有可能导致丢失1s内的事务
-
-
redo buffer还和操作系统缓存机制有关,所以刷写策略可能和==innodb_flush_method==参数有一定关系
redo也有group commit;可以理解为,每次刷新已提交的redo时,顺便可以将一些未提交的事务redo也一次性刷写到磁盘。此时为了区分不同状态的redo,会加一些比较特殊的标记(是否提交标记)
-
undo logs
-
作用
- 在事务ACID过程中,实现的是“A” 原子性的作用
- 另外CI也依赖于Undo
- 在rolback时, 将数据恢复到修改之前的状态
- 在CSR实现的是, 将redo当中记录的未提交的时候进行回滚.
- ==CSR=REDO+UNDO(先前滚或回滚)==
- undo提供快照技术 保存事务修改之前的数据状态. 保证了MVCC, 隔离性, mysqldump的热备
- 每个事务开启时(begin),都会通过undo生成一个一致性的快照
- ==MVCC(一致性快照读取/一致性非锁定锁)==---每次开启一个全新的事务窗口(begin),都会生成当前最新的一致性(事务)快照(undo),直到事务commit或者rollback。此次事务,都会在此快照中进行操作
- 可以大大提高事务的并发能力
- undo在生成过程中,也会被记录在redo信息里
-
-
隔离级别
- 作用
- 主要是提供I的特性,另外对于C的特性也有保证
- 事务隔离性介绍(transaction isolation)
- RU:读未提交
- RC:读已提交
- RR:可重复读(默认级别)
- SR:可串行化
- 这里的读不是sql层的数据行的select,而指的是存储引擎的读,是page的读取
- ==隔离级别说明==
- RU(READ UNCOMMITTED):读未提交
- 出现的问题:脏页读,不可重复读,幻读
- RC(READ COMMITTED):读已提交
- 出现的问题:不可重复读,幻读
- RR(REPEATABLE READ ):可重复读(默认级别)
- 出现的问题:
有可能出现
幻读 - ==防止不可重复读现象:利用的就是undo的一致性快照读。MVCC重要功能==
- 通过RR,已经可以解决99%以上的幻读,为了更加严谨,加入了==GAP(间隙锁)和next-lock(下一键锁)==锁定功能来预防幻读
- 出现的问题:
- SR(SERIALIZABLE):可串行化
- 串行化事务。以上问题都能规避,但是不利于事务的并发
- RU(READ UNCOMMITTED):读未提交
- 参数修改
- ==select @@transaction_isolation;==
- ==set global transaction_isolation='read-uncommitted';==
- vi /etc/my.cnf
- 作用
-
锁机制
- 作用
- 保证事务之间的隔离性(主要提供写隔离),也保证了数据的一致性
- 保证资源不被争用。锁是属于资源的,不是某个事务的特性
- 每次事务需要资源的时候,需要申请持有资源的锁
- 锁类型
- 资源
- 内存锁:mutex,latch,保证内存数据页资源不被争用,不被置换
- ==对象锁==:
- mdl(元数据锁):修改元数据时。DDL---》alter、备份
- table_lock:表锁,DDL,备份(FTWRL全局表锁),==lock table table_name read==
- record(low)lock:行锁,索引锁,锁定聚簇索引
- GAP:间隙锁,RR级别,普通辅助索引间隙锁
- Next-lock:下一键锁,GAP+record lock,普通辅助索引的范围锁
- 对象锁的粒度
- mdl(元数据锁):修改元数据时。DDL---》alter、备份
- table_lock:表锁,DDL,备份(FTWRL全局表锁),==lock table table_name read==,可能由下面的锁变成表锁
- IS
- IX
- ==如果都没有索引,update语句就是表锁==
- record(low)lock:行锁,索引锁,锁定聚簇索引
- GAP:间隙锁,RR级别,普通
辅助
索引间隙锁 - Next-lock:下一键锁,GAP+record lock,普通
辅助
索引的范围锁
- 功能分类
- IS:意向共享锁,表级别
- S:共享锁,读锁,行级别
- IX:意向排他锁,表级别
- ==X:排他锁,写锁,行级别==
-
- https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
- 资源
- 作用
### transaction_isolation='read-uncommitted'
#### 脏读(两个会话)
##### A session
begin;
update city set name='xxx' where id=2;
##### B session
begin;
select * from city;
##### 在session B事务中读取到了,session A未提交数据的脏数据
#### 不可能重复读
##### session B中的事务中,执行相同查询命令时,读到了session A正在发生变化的数据
#### 幻读
#### 在一个事务窗口中,更新操作,出现了别的插入数据的幻行
==innodb的事务总结==
==ACID---redo+undo+隔离级别(读隔离)+mvcc(防止不可重复读)+锁(写隔离)==
A:原子性---undo ,redo(CSR,uncommited)---写
D:持久性---redo(commit的数据,WAL)---写
I:隔离性---隔离级别,锁,MVCC(undo的一致性快照/一致性非锁定锁)---读和写
C:保证事务工作前,中,后,数据的状态都是完整的,一致的
写一致性:undo,redo,lock
读一致性:隔离级别(isolation level),MVCC(undo)
数据页一致性:double write buffer(磁盘区域)
Double write 是InnoDB在 tablespace上的128个页(2个区)是2MB;
其原理:
为了解决 partial page write 问题 ,当mysql将脏数据flush到data file的时候, 先使用memcopy 将脏数据复制到内存中的double write buffer ,之后通过double write buffer再分2次,每次写入1MB到共享表空间,然后马上调用fsync函数,同步到磁盘上,避免缓冲带来的问题,在这个过程中,doublewrite是顺序写,开销并不大,在完成doublewrite写入后,在将double write buffer写入各表空间文件,这时是离散写入。
如果发生了极端情况(断电),InnoDB再次启动后,发现了一个Page数据已经损坏,那么此时就可以从doublewrite buffer中进行数据恢复了。
==存储引擎核心参数==
==innodb_flush_log_at_trx_commit=0/1/2==
1,每次事物的提交都会引起日志文件写入、flush磁盘的操作,确保了事务的ACID;flush 到操作系统的文件系统缓存 fsync到物理磁盘.
0,表示当事务提交时,不做日志写入操作,而是每秒钟将log buffer中的数据写入文件系统缓存并且秒fsync磁盘一次;
2,每次事务提交引起写入文件系统缓存,但每秒钟完成一次fsync磁盘操作。
--------
The default setting of 1 is required for full ACID compliance. Logs are written and flushed to disk at each transaction commit.
With a setting of 0, logs are written and flushed to disk once per second. Transactions for which logs have not been flushed can be lost in a crash.
With a setting of 2, logs are written after each transaction commit and flushed to disk once per second. Transactions for which logs have not been flushed can be lost in a crash.
-------
==Innodb_flush_method=fsync/O_DIRECT/O_DSYNC==
https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_flush_method
O_DIRECT :数据缓冲区写磁盘,不走OS buffer
fsync :日志和数据缓冲区写磁盘,都走OS buffer
O_DSYNC :日志缓冲区写磁盘,不走 OS buffer
### 生产建议使用O_DIRECT,最好是配合固态盘使用
==innodb_buffer_pool_size==
https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_buffer_pool_size
show engine innodb status\G;
一般建议最多是物理内存的 75-80%
作用:数据缓冲区的总大小。缓冲数据页和索引页。是mysql最大的内存区域
### 默认128M
第十天
工具日志管理
错误日志
-
作用
- 记录mysql从启动后,所有的状态、警告、错误
- 为我们定位数据库问题,提供帮助
默认:开启
select @@log_error; show variables like 'log_error';
-
定制方法
vim /etc/my.cnf log_error=/tmp/mysql.log
binlog二进制日志
实际上是我们数据恢复时配合备份一起恢复数据的手段
-
作用
- 数据恢复,主从复制中应用
- 主要记录数据库变化(DDL,DCL,DML)性质的日志。是==逻辑层==性质日志
-
配置
默认:8.0版本之前,没有开启。建议生产开启
vi /etc/my.cnf server_id=xxx #主机编号。 log_bin=/data/binlog/mysql-bin #日志存放目录/日志名前缀,例如:mysql-bin.000001 mysql-bin.000002 sync_binlog=1 #binlog日志刷盘策略,双一中的的第二个1.每次事务提交立即刷写binlog到磁盘 binlog_format=row #binlog的记录格式为row模式 #expire_logs_days #一定要和数据盘分开 mkdir -p /data/binlog chown -R mysql.mysql !$ /etc/init.d/mysqld restart ls -l /data/binlog
-
记录内容是啥?
- binlog是==SQL层==的功能。记录的是变更SQL语句,
不记录
查询语句。 - 种类
- DDL :原封不动的记录当前DDL(statement语句方式)。
- DCL :原封不动的记录当前DCL(statement语句方式)。
- DML :
只记录已经提交
的事务DML(insert,update,delete)
- DML三种记录方式
- binlog_format(binlog的记录格式)参数影响
- statement(5.6默认)SBR(statement based replication) :语句模式原封不动的记录当前DML。
- ROW(5.7 默认值) RBR(ROW based replication) :记录数据行的变化(用户看不懂,需要工具分析)
- mixed(混合)MBR(mixed based replication)模式 :以上两种模式的混合
- binlog是==SQL层==的功能。记录的是变更SQL语句,
-
event(事件)
二进制日志的最小记录单元
对于DDL,DCL,一个语句就是一个event
对于DML语句来讲,只记录已提交的事务
-
组成
- 事件的开始标识(position)
- 事件内容
- 事件的结束标识
-
标识
- 某个事件在binlog中的相对位置号
- 位置号的作用是什么?
- 为了方便我们截取事件
-
binlog的查看
- 查看开启情况
- ==select @@log_bin_basename;==
- ==select @@log_bin;==
- ==show variables like '%log_bin%';==
- 事件查看命令
- ==show binary logs;==
- ==show master status;==
- ==show binlog events in 'mysql-bin.000002';==
- ==mysql -e "show binlog events in 'mysql-bin.000004'" |grep drop==
- 内容查看命令
- ==mysqlbinlog /data/mysql/mysql-bin.000006==
- ==mysqlbinlog --base64-output=decode-rows -vvv /data/binlog/mysql-bin.000003==
- mysqlbinlog -d binlog /data/binlog/mysql-bin.000003
- ==-d database_name==
- mysqlbinlog --start-datetime='2019-05-06 17:00:00' --stop-datetime='2019-05-06 17:01:00' /data/binlog/mysql-bin.000004 /data/binlog/mysql-bin.000005
- 查看开启情况
-
日志截取恢复
- ==flush logs;==
- 滚动一个新的binlog日志
- ==mysqlbinlog --start-position=219 --stop-position=1347 /data/binlog/mysql-bin.000003 >/tmp/bin.sql==
- set sql_log_bin=0/1;
- ==flush logs;==
-
==维护操作==
- 日志滚动
- ==flush logs;==
- ==mysqladmin -uroot -pxxx flush-logs==
- ==select @@max_binlog_size;==
- 默认:1G
- mysqldump -F
- 重启数据库
- 日志删除
- 不能用rm
- ==select @@expire_logs_days;==
- 自动删除机制
- 默认:0天,代表永不删除
- 一般生产建议最少2个全备周期+1
- ==purge binary logs to ‘mysql-bin.000005’;==
- 手工删除
- ==purge binary logs befor '2021-02-01 22:46:46';==
- ==reset master;==
- 全部清空
- 日志滚动
slow慢日志
-
作用
- 记录mysql运行过程中较慢的语句,通过一个文本的文件记录下来的
- 帮助我们进行语句优化工具日志
-
如何配置
- 默认没有开启
- ==select @@slow_query_log;==
- ==select @@slow_query_log_file;==
- ==select @@long_query_time;==
- 默认:10S
- ==select @@log_queries_not_using_indexes;==
-
==配置参数例子==
vi /etc/my.cnf #开关: slow_query_log=1 #文件位置及名字 slow_query_log_file=/data/mysql/slow.log #设定慢查询时间: long_query_time=0.1 #没走索引的语句也记录:和上面是或的关系 log_queries_not_using_indexes=1
SBR与RBR模式的对比
STATEMENT:可读性较高,日志量少,但是不够严谨
ROW :可读性很低,日志量大,足够严谨
update t1 set xxx=xxx where id>1000 ? -->一共500w行,row模式怎么记录的日志
为什么row模式严谨?
id name intime
insert into t1 values(1,'zs',now())
我们建议使用:row记录模式
Master [binlog]>show binlog events in 'mysql-bin.000003'; +------------------+-----+----------------+-----------+-------------+----------------------------------------+ | Log_name | Pos | Event_type | Server_id | End_log_pos | Info | +------------------+-----+----------------+-----------+-------------+----------------------------------------+ | mysql-bin.000003 | 4 | Format_desc | 6 | 123 | Server ver: 5.7.20-log, Binlog ver: 4 | | mysql-bin.000003 | 123 | Previous_gtids | 6 | 154 | | | mysql-bin.000003 | 154 | Anonymous_Gtid | 6 | 219 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | mysql-bin.000003 | 219 | Query | 6 | 319 | create database binlog | | mysql-bin.000003 | 319 | Anonymous_Gtid | 6 | 384 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | mysql-bin.000003 | 384 | Query | 6 | 486 | use `binlog`; create table t1 (id int) | +------------------+-----+----------------+-----------+-------------+----------------------------------------+ Log_name:binlog文件名 Pos:开始的position ***** Event_type:事件类型 Format_desc:格式描述,每一个日志文件的第一个事件,多用户没有意义,MySQL识别binlog必要信息 Server_id:mysql服务号标识 End_log_pos:事件的结束位置号 ***** Info:事件内容***** 补充: SHOW BINLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count] [root@db01 binlog]# mysql -e "show binlog events in 'mysql-bin.000004'" |grep drop mysqlbinlog /data/mysql/mysql-bin.000006 mysqlbinlog --base64-output=decode-rows -vvv /data/binlog/mysql-bin.000003 mysqlbinlog -d binlog /data/binlog/mysql-bin.000003 [root@db01 binlog]# mysqlbinlog --start-datetime='2019-05-06 17:00:00' --stop-datetime='2019-05-06 17:01:00' /data/binlog/mysql-bin.000004
基于Position号进行日志截取
### 核心就是找截取的起点和终点 --start-position=321 --stop-position=513 mysqlbinlog --start-position=219 --stop-position=1347 /data/binlog/mysql-bin.000003 >/tmp/bin.sql ### 案例: 使用binlog日志进行数据恢复 #### 模拟: 1. [(none)]>create database binlog charset utf8; 2. [(none)]>use binlog; [binlog]>create table t1(id int); 3. [binlog]>insert into t1 values(1); [binlog]>commit; [binlog]>insert into t1 values(2); [binlog]>commit; [binlog]>insert into t1 values(3); [binlog]>commit; 4. [binlog]>drop database binlog; #### 恢复: [(none)]>show master status ; [(none)]>show binlog events in 'mysql-bin.000004'; [root@db01 binlog]# mysqlbinlog --start-position=1227 --stop-position=2342 /data/binlog/mysql-bin.000004 >/tmp/bin.sql [(none)]>set sql_Log_bin=0; [(none)]>source /tmp/bin.sql ### 面试案例: 1. 备份策略每天全备,有全量的二进制日志 2.业务中一共10个库,其中一个被误drop了 3. 需要在其他9个库正常工作过程中进行数据恢复
==双一说明(双一标准)==
- innodb_flush_log_at_trx_commit=1
- 每次提交事务,必然log buffer中的redo落到磁盘
- sync_binlog=1
- 每次事务提交,必然保证binlog cache中的日志落到磁盘
binlog的GTID模式管理
介绍
5.6 版本新加的特性,5.7中做了加强
5.6 中不开启,没有这个功能.
5.7 中的GTID,即使不开也会有自动生成
==SET @@SESSION.GTID_NEXT= 'ANONYMOUS'==
是对于一个已提交事务的编号,并且是一个全局唯一的编号。
它的官方定义如下:GTID =server_uuid :transaction_id
7E11FA47-31CA-19E1-9E56-C43AA21293967:29
GTID(Global Transaction ID)
-
重要参数
### 开启gtid vim /etc/my.cnf gtid-mode=on enforce-gtid-consistency=true systemctl restart mysqld Master [(none)]>create database gtid charset utf8; Query OK, 1 row affected (0.01 sec) Master [(none)]>show master status ; +------------------+----------+--------------+------------------+----------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+----------------------------------------+ | mysql-bin.000004 | 326 | | | dff98809-55c3-11e9-a58b-000c2928f5dd:1 | +------------------+----------+--------------+------------------+----------------------------------------+ 1 row in set (0.00 sec) Master [(none)]>use gtid Database changed Master [gtid]>create table t1 (id int); Query OK, 0 rows affected (0.01 sec) Master [gtid]>show master status ; +------------------+----------+--------------+------------------+------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+------------------------------------------+ | mysql-bin.000004 | 489 | | | dff98809-55c3-11e9-a58b-000c2928f5dd:1-2 | +------------------+----------+--------------+------------------+------------------------------------------+ 1 row in set (0.00 sec) Master [gtid]>create table t2 (id int); Query OK, 0 rows affected (0.01 sec) Master [gtid]>create table t3 (id int); Query OK, 0 rows affected (0.02 sec) Master [gtid]>show master status ; +------------------+----------+--------------+------------------+------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+------------------------------------------+ | mysql-bin.000004 | 815 | | | dff98809-55c3-11e9-a58b-000c2928f5dd:1-4 | +------------------+----------+--------------+------------------+------------------------------------------+ 1 row in set (0.00 sec) Master [gtid]>begin; Query OK, 0 rows affected (0.00 sec) Master [gtid]>insert into t1 values(1); Query OK, 1 row affected (0.00 sec) Master [gtid]>commit; Query OK, 0 rows affected (0.00 sec) Master [gtid]>show master status ; +------------------+----------+--------------+------------------+------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+------------------------------------------+ | mysql-bin.000004 | 1068 | | | dff98809-55c3-11e9-a58b-000c2928f5dd:1-5 | +------------------+----------+--------------+------------------+------------------------------------------+ 1 row in set (0.00 sec) Master [gtid]>begin; Query OK, 0 rows affected (0.00 sec) Master [gtid]>insert into t2 values(1); Query OK, 1 row affected (0.00 sec) Master [gtid]>commit; Query OK, 0 rows affected (0.01 sec) Master [gtid]>show master status ; +------------------+----------+--------------+------------------+------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+------------------------------------------+ | mysql-bin.000004 | 1321 | | | dff98809-55c3-11e9-a58b-000c2928f5dd:1-6 | +------------------+----------+--------------+------------------+------------------------------------------+ 1 row in set (0.00 sec)
-
==select @@gtid_mode;==
- ==select @@enforce-gtid-consistency;==
- ==show master status;==
select @@server_uuid;
基于GTID进行查看binlog
- ==--include-gtids==
- ==--exclude-gtids==
### 具备GTID后,截取查看某些事务日志:
--include-gtids
--exclude-gtids
mysqlbinlog --include-gtids='dff98809-55c3-11e9-a58b-000c2928f5dd:5-16' --exclude-gtids='dff98809-55c3-11e9-a58b-000c2928f5dd:4' /data/binlog/mysql-bin.000004
GTID的幂等性
- ==--skip-gtids==
### 开启GTID后,MySQL恢复Binlog时,重复GTID的事务不会再执行了
#### 就想恢复?怎么办?
--skip-gtids
mysqlbinlog --skip-gtids --include-gtids='dff98809-55c3-11e9-a58b-000c2928f5dd:5-16' --exclude-gtids='dff98809-55c3-11e9-a58b-000c2928f5dd:4' /data/binlog/mysql-bin.000004 /data/binlog/mysql-bin.000005 >/tmp/binlog-gtid.sql
set sql_log_bin=0;
source /tmp/binlog-gtid.sql
set sql_log_bin=1;
mysqldumpslow 分析慢日志
mysqldumpslow -s c -t 10 /data/mysql/slow.log
#### -s 排序 c 次数 -t top
### 第三方工具(自己扩展)
https://www.percona.com/downloads/percona-toolkit/LATEST/
yum install perl-DBI perl-DBD-MySQL perl-Time-HiRes perl-IO-Socket-SSL perl-Digest-MD5
#### toolkit工具包中的命令:
./pt-query-diagest /data/mysql/slow.log
#### Anemometer基于pt-query-digest将MySQL慢查询可视化
复习命令
- show master status;
- show binlog events in 'mysql-bin.000001' limit 100;
- mysql -e "show binlog events in 'mysql-bin.000001'" |grep xxx
- mysqlbinlog --start-position x --stop-position xx mysql-bin.000001 > /tmp/binlog.sql
- mysqlbinlog -d database_name -start-position x --stop-position xx mysql-bin.000001 > /tmp/binlog.sql
- mysqlbinlog --start-datetime x --stop-datetime xx mysql-bin.000001 mysql-bin.000002 > /tmp/binlog.sql
- mysqlbinlog --skip-gtids --include-gtids='servier-uuid:2-19' --exclude-gtids='server-uuid:10-11' mysql-bin.000001 mysql-bin.000002 > /tmp/gtid.sql
- mysqlbinlog --skip-gtids --include-gtids='servier-uuid:2-9','server-uuid:12-19' mysql-bin.000001 mysql-bin.000002 > /tmp/gtid.sql
- mysqlbinlog --skip-gtids --include-gtids='servier-uuid:2-19' mysql-bin.000001 mysql-bin.000002 > /tmp/gtid.sql
- set sql_log_bin=0;
- flush logs;
- purge binary logs to ......;
- mysqldumpslow -s c -t N xxx-slow.log