Mysql45讲学习笔记

前言:这篇文章主要总结事务,锁、索引的一些知识点,然后分享一下自己学习小心得,我会从点到线在到面展开说说,对于学习任何知识,我们都应该藐其全貌,不要一开始就选入细节

基础

一、基础架构:一条查询sql是怎么执行的?

在这里插入图片描述

二、 日志系统:一条更新语句是怎么执行的?

1.redolog与binlog区别?

① redolog是innodb存储引擎实现,而binlog是在Server层实现

② redolog是物理存储,而binlog是逻辑存储

③ redolog是循环写入(空间会用尽,所以每隔一段时间就需要擦除),而binlog是追加写入(所以说他是归档日志)

2、二阶段提交是怎么样?

3、第一步:写入redolog处于准备阶段

4、第二步:写入binlog

5、第三步:redolog提交阶段、

备注:将 redo log 的写入拆成了两个步骤:prepare 和 commit,这就是"两阶段提交"。

我的理解

从 “两阶段提交”的执行流程看,“ binlog 成功,redo log失败”的场景(备注:数据库 crash-重启后,会对记录对redo log进行检查 )

1、如果 redo log 已经commit,则视为有效。

2、如果 redo log prepare 但未commit,则check对应的bin log记录是否记录成功。

① bin log记录成功则将该prepare状态的redo log视为有效

② bin log记录不成功则将该prepare状态的redo log视为无效

img

3、一些参数补充说明

binlog日志模块

sync_binlog=1的时候,表示每次事务的 binlog 都持久化到磁盘(建议设置成1)

补充:binlog一共有三种模式

① statement模式:记录sql语句,优点占用内存少,如果出现函数可能到回到数据不一致

② row模式:直接记录数据,占用内存大

③ mixed:以上两种模式的混合

redolog日志模块

innodb_flush_log_at_trx_commit={0|1|2} (指定何时将事务日志刷到磁盘,默认为1)

0表示每秒将"log buffer"同步到"os buffer"且从"os buffer"刷到磁盘日志文件中。

1表示每事务提交都将"log buffer"同步到"os buffer"且从"os buffer"刷到磁盘日志文件中。

2表示每事务提交都将"log buffer"同步到"os buffer"但每秒才从"os buffer"刷到磁盘日志文件中。

问题:前面我说到定期全量备份的周期“取决于系统重要性,有的是一天一备,有的是一周一备”。那么在什么场景下,一天一备会比一周一备更有优势呢?或者说,它影响了这个数据库系统的哪个指标?

这个需要根据业务进行评估,空间换时间

三、 事务隔离:为什么你改了我还是看不见?

1、Mysql有哪些隔离级别?

① 读未提交:别人改数据的事务尚未提交,我在我的事务中也能读到。

② 读已提交:别人改数据的事务已经提交,我在我的事务中才能读到。

③ 可重复读:别人改数据的事务已经提交,我在我的事务中也不去读。

④ 串行:我的事务尚未提交,别人就别想改数据

2、事务隔离的实现

RC:每次查询的时候都会去创建一个视图

RR:事务启动的时候就会创建视图

RU:不存在视图

串行化:不存在视图这个概念,都是通过加锁方式实现事务的隔离

3、事务启动方式

① 把自动提交关掉:set autocommit=0

② 例子->显示启动事务

begin;
update t1 set a=1 where id = 2;
commit;
rollback;

这里有个需要注意的点:begin的时候是还没启动事务,在执行第一条sql才会启动

③ 如何查询长事务,查询持续时间超过60秒的事务

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60; 

*4、**问题:如何避免长事务对业务的影响?*

1、set global general_log=on;开启通用日志,在业务功能测试阶段要求输出所有的 general_log,分析日志行为提前发现问题

1、确认是否有不必要的只读事务。把select语句放在事务外

2、通过 SET MAX_EXECUTION_TIME 命令,避免单sql执行时间过长

3、监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill;

4、innodb_undo_tablespaces设置成 2,即是不是用系统表空间,使用独立空间,如果真的出现大事务导致回滚段过大,这样设置后清理起来更方便

四、 深入浅出索引(上)

总结:

*第一点:索引模型的选择*

1.索引的作用:提高数据查询效率

2.常见索引模型:哈希表、有序数组、搜索树

3.哈希表:键 - 值(key - value)。

4.哈希思路:把值放在数组里,用一个哈希函数把key换算成一个确定的位置,然后把value放在数组的这个位置

5.哈希冲突的处理办法:链表

6.哈希表适用场景:只有等值查询的场景

7.有序数组:按顺序存储。查询用二分法就可以快速查询,时间复杂度是:O(log(N))

8.有序数组查询效率高,更新效率低 9.有序数组的适用场景:静态存储引擎。

10.二叉搜索树:每个节点的左儿子小于父节点,父节点又小于右儿子

11.二叉搜索树:查询时间复杂度O(log(N)),更新时间复杂度O(log(N))

12.数据库存储大多不适用二叉树,因为树高过高,会适用N叉树

*第二点:Innodb的索引模型介绍*

\1. InnoDB中的索引模型:B+Tree

2.索引类型:主键索引、非主键索引 主键索引的叶子节点存的是整行的数据(聚簇索引),非主键索引的叶子节点内容是主键的值(二级索引)

3.主键索引和普通索引的区别:主键索引只要搜索ID这个B+Tree即可拿到数据。普通索引先搜索索引拿到主键值,再到主键索引树搜索一次(回表)

\4. 一个数据页满了,按照B+Tree算法,新增加一个数据页,叫做页分裂,会导致性能下降。空间利用率降低大概50%。当相邻的两个数据页利用率很低的时候会做数据页合并,合并的过程是分裂过程的逆过程。

5.从性能和存储空间方面考量,自增主键往往是更合理的选择。

*问题:说说下面重建索引的一些方案是否合理?*

*1.重建k索引*

*alter table T drop index k;*

*alter table T add index(k);*

*2.重建主键索引*

*alter table T drop primary key;*

*alter table T add primary key(id);*

*重建索引 k 的做法是合理的,可以达到省空间的目的。但是,重建主键的过程不合理。不论是删除主键还是创建主键,都会将整个表重建。所以连着执行这两个语句的话,第一个语句就白做了。这两个语句,你可以用这个语句代替 : alter table T engine=InnoDB*

五、 深入浅出索引(下)

总结:

\1. 覆盖索引:如果查询条件使用的是普通索引(或是联合索引的最左原则字段),查询结果是联合索引的字段或是主键,不用回表操作,直接返回结果,减少IO磁盘读写读取正行数据

\2. 最左前缀:联合索引的最左 N 个字段,也可以是字符串索引的最左 M 个字符

\3. 联合索引:根据创建联合索引的顺序,以最左原则进行where检索,比如(age,name)以age=1 或 age= 1 and name=‘张三’可以使用索引,单以name=‘张三’ 不会使用索引,考虑到存储空间的问题,还请根据业务需求,将查找频繁的数据进行靠左创建索引。

\4. 索引下推:like 'hello%’and age >10 检索,MySQL5.6版本之前,会对匹配的数据进行回表查询。5.6版本后,会先过滤掉age<10的数据,再进行回表查询,减少回表率,提升检索速度

*问题:*

*CREATE TABLE geek (*

*a int(11) NOT NULL,*

*b int(11) NOT NULL,*

*c int(11) NOT NULL,*

*d int(11) NOT NULL,*

*PRIMARY KEY (a,b),*

*KEY c (c),*

*KEY ca (c,a),*

*KEY cb (c,b)*

*) ENGINE=InnoDB;*

*针对下面两条sql,以上那个索引可以不用,为什么*

*select * from geek where c=N order by a limit 1;*

*select * from geek where c=N order by b limit 1;*

*答案:ca索引可以不用,永久记住最左匹配,以最左为主*

六、 全局所有和表锁:给表加个字段有这么多障碍吗?

1. *MySQL从加锁范围上分为哪三类?*

全局锁、表级锁、行级锁

*2. 全局锁加锁方法的执行命令是什么?主要的应用场景是什么?*

flush tables with read lock 应用场景:全库逻辑备份

*3. 做整库备份时为什么要加全局锁?*

不加锁的话,系统备份得到的库,不是同一个逻辑时间点的,会导致数据不一致。

*4. MySQL的自带备份工具, 使用什么参数可以确保一致性视图, 在什么场景下不适用?*

使用参数:-single-transaction 只适用于所有表使用事务引擎的库,部分表使用的引擎不支持事务的话,不能用该方法。

*5. 不建议使用set global readonly = true的方法加全局锁有哪两点原因?*

① 有些系统用readonly判断是主库还是备库,修改这个值对整个系统影响太大;

② 如果客户端发生异常,数据库就会一直保持readonly,会导致长时间无法写入数据,风险很高。

*6. 表级锁有哪两种类型? 各自的使用场景是什么?*

①表锁。lock tables xxx read/write。数据库引擎不支持行锁时,才会用到表锁。

②元数据锁。MDL,分为MDL 读锁和 MDL 写锁。执行DML的时候,会申请 MDL读锁;执行DDL的时候,会申请 MDL写锁。

7. *MDL中读写锁之间的互斥关系怎样的?*

读读共享,读写互斥,写写互斥。

*8. 如何安全的给小表增加字段?*

①减少长事务,避免跟长事务竞争 MDL锁,如果获取 MDL写锁阻塞,会影响后面 MDL读锁获取,导致所有会话阻塞。

②Alter table语句,设置超时时间,超过时间未获取到 MDL写锁,则放弃,后面再进行重试,避免影响后面的会话。

问题:

备份一般都会在备库上执行,你在用–single-transaction 方法做逻辑备份的过程中,如果主库上的一个小表做了一个 DDL,比如给一个表上加了一列。这时候,从备库上会看到什么现象呢?

DDL binlog同步到备库后,此时备库有MDL读锁,而同步过来的DDL变更到备库上需要MDL写锁,那么这个DDL会被阻塞,所以该DDL不会反应在备份的数据里面。当使用该备份数据进行恢复时,由于加了一列,那么恢复会异常

七、 行锁的功过:怎么减少行锁对性能的影响?

\1. 行锁两阶段协议:不是事务开始的时候开启,而是语句执行的时候上锁

这个就涉及到插入和更新sql的先后顺序去优化减少锁冲突

\2. 死锁:互相持有对方资源

\3. 死锁检测优劣,怎么避免大量死锁检测、高并发下避免死锁检测带来的负面影响:

① 确保业务上不会产生死锁,直接将死锁检测关闭。(innodb 自带死锁检测)

② 在数据库中间件中统一对更新同一行的请求进行排队,控制并发度。

③ 业务逻辑上进行优化,将一行数据分解成多行,降低写入压力。

八、 事务到底是隔离还是不隔离?

\1. mysql中有两个视图的概念

① 一个是view 用来创建虚表

② 另一个一致性视图,主要用来解决rr和rc问题

  1. “快照”在 MVCC 里是怎么工作的?

参考事务隔离流程图

实战

一、唯一索引和普通索引怎么选?

1、唯一索引和普通索引怎么选?

① 查询情况下,普通索引和唯一索引性能都差不多,因为mysql以页形式读到内存,在内存判断是很快的

② 更新情况下,唯一索引没有用到change buffer,而普通索引有用到,因为唯一索引就一条记录,如果内存有,直接就可以在内存操作,就没必要用change buffer

③ chang buffer与merge结合使用,有change buffer如果数据不存在,就不用重新查磁盘,直接写入到change buffer,等一下次查询就合并磁盘和增量修改,或者定时merge增量修改

④ 所以怎么选,写入比较频繁的建议用普通索引,如果是写完马上读,会触发merge,io次数不会减少,反而增加change buffer维护代价

2、merge 的执行流程是这样的:

① 从磁盘读入数据页到内存(老版本的数据页);

② 从 change buffer 里找出这个数据页的 change buffer 记录 (可能有多个),依次应用,得到新版数据页;

③ 写 redo log。这个 redo log 包含了数据的变更和 change buffer 的变更。

问题:如果某次写入使用了 change buffer 机制,之后主机异常重启,是否会丢失 change buffer 和数据。

这个问题的答案是不会丢失,虽然是只更新内存,但是在事务提交的时候,我们把 change buffer 的操作也记录到 redo log 里了,所以崩溃恢复的时候,change buffer 也能找回来。

二、mysql为什么有时候会选错索引?

*1、为什么有时候mysql会选错索引?*

扫描的行数过多情况下,优化器会选择扫描全表而不用索引

*2、解决办法*

通过explain sql后发现选错索引,可以采取以下方式:

① 通过指定索引 force index

② 通过sql优化让其走预期的索引

③ 删除不必要的索引

备注:这种情况生产上是比较少发生,索引不必过多关注

三、怎么给字符串加索引?

*1.为什么需要优化字符串上的索引*

如果字符串较长,索引字段占用内存空间大,B+树高度较高,这样查询IO次数较多,耗时长。 个人认为未出现性能瓶颈,不需要过度优化,全字段索引也ok。

*2.优化方法及优缺点、适用场景*

方法一:前缀索引 概念:在建立索引的时候指定索引长度,且该长度的字段区分度高

优点:a. 相比全字段索引,每页存储的索引更多,查询索引IO次数少,效率高。即既节省了内存空间,又提高了查询效率。

缺点:

a. 指定索引长度的区分度低的话,扫描主键索引次数就会多,效率低;

b. 不会使用覆盖索引,即使索引长度定义为全字段,也会去主键索引查询 适用场景:前缀索引区分度高

方法二:倒序存储 概念:字段保存的时候反序存储 优点:同前缀索引

缺点:

a.只适用于等值查询,不适用于范围、模糊查询。

b.每次保存、查询时需调用reverse()函数;

c.若后缀索引区分度低,扫描行数会增多。 适用场景:索引字段后缀区分度高,前缀区分度低。

方法三:添加hash字段,作为索引 概念:在表中添加一个hash字段并加索引,用于存储索引字段的哈希值如使用crc32()哈希函数,每次查询时先计算出字段的hash值,再利用hash字段查询。可能存在hash冲突,所以where需要加索引字段字段的等值条件。

优点:哈希函数冲突概率低的话,平均扫描行数接近1。

缺点:只适用于等值查询,不适用于范围、模糊查询。 适用场景:只适用于等值查询,不适用范围查询或模糊查询。

四、为什么我的mysql会抖一下

*1、首先,理解一下什么是脏页,什么是干净页*

① 脏页:内存与磁盘的内容不一致的时候,我们称这个内存也就是脏页

② 干净页:内存写入磁盘后,内容一致了,该内存页就是干净页

深层理解:每个表都是一个ibd文件, 每个文件都是分成n个16kb的page,page是IO的基本单位, 也就是从硬盘到内存每次都载入一个page,所以用到的page既在内存也在硬盘ibd文件里. 在内存page上写写改改后, 这个page没写回ibd文件, 就成了脏页

2、*那么,什么时候会刷脏页呢?*

*redolog写满了*:redo log是在同一块地方进行循环的写,redo log记录的变动会被清除,在清除时可能redo log中有记录变更的数据还未刷入磁盘中,这时就得需要去判断这些变动的数据是否刷入磁盘,没有则进行刷脏页

*内存满了*:内存不够用的时候,就要淘汰一些数据页,空出内存给别的数据页使用。如果淘汰的是“脏页”,就要先将脏页写到磁盘

③ 系统空闲的时候

④ mysql正常关闭

说明第②点为什么要刷脏页:反证法->如果内存满了不刷脏页到磁盘中,下次请求磁盘中的干净页到内存时,还是要额外判断redolog是否对该也有修改,有修改的话还是要刷到磁盘中,这样还不如在内存满了的时候,直接将它刷到磁盘中

*3、InnoDB 刷脏页的控制策略?*

*影响因素有:*

\1. 脏页比例

\2. 脏页刷盘速度

\3. 刷新相邻页面策略 (bufferpool脏页比例 或 redolog 都可能成为读写sql的瓶颈)

*参数控制:*

\1. 脏页比例默认75%,一定不要让其接近75%=脏页/总页

\2. 刷脏页速度 innodb_io_capacity定义的能力乘以R%来控制刷脏页的速度

\3. innodb_flush_neighbors=0(不开启脏页相邻淘汰) (对于机械硬盘顺序读写会有提升,ssd无提升,mysql8直接默认不开启)

\4. 避免大量刷脏页,脏页flush可能会产生内存抖动

五、为什么数据删减一半,表文件大小不变?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值