整理了一下之前面试,学习,总结的知识如果觉得有用的话可以关注一下我的的语雀文档
https://www.yuque.com/g/hnsqls/rkzi78/ovpy9dgdnuw0yu04/collaborator/join?token=dVoOmnNfF0y47fLv&source=doc_collaborator#
以及 Github:https://github.com/hnsqls/interview
最近一个月的主要内容就是找工作面试,所以分享出来一是督促自己记录学习,二是希望产出点有用的东西,让大家共同学习进步。
1.Mysql的中从同步原理
主从的模式
主从同步的原理
核心就是binlog日志,Mysql的主库在事务提交时会有个binlog日志记录数据库的变化,从库有个线程IOthread去读去主库的binlog日志写入到从库的replylog日志中。然后从库的线程SqlThread执行relylog日志实现主从同步。
- 主库开启binlog 日志
- 主库的增删改操作,都会记录到binlog日志中。
- 从库感知主库binlog的binlog日志的变化,每当发生改变,启用一个IO线程去读取主库binlog文件,并记录下来存在 Relay log(中继文件)中。
- 最后利用Relay log 重放主库的bin log,完成数据同步。
tips:
1. `bin log` 日志怎么开启?
2. 从库怎么感知主库的binlog 变化?
3. `bin log` 日志到底是什么,怎么能重放就能完成数据同步?
MySQL 的Binlog,它记录了所有的 DDL 和 DML语句,也就是记录了数据库中数据的变化。
bin log
的应用
- 数据复制(主从同步)。
- 数据恢复(误操作恢复、灾难恢复)。
- 变更数据捕获(实时数据同步、数据异构)。
- 审计和日志分析
Bin log 日志
- 如何开启binlog 日志
MySQL 5. 7
默认情况下是不开启Binlog,因为记录Binlog日志需要消耗时间,官方给出的数据是有1%的性能损耗。最主要的是,早期阶段,那个时期往往是单机mysql。往往不需要进行数据复制,而且开启bin log 还要一些格外的配置
比如,设置 server_id
:主从复制时,每个 MySQL 实例需要唯一的 server_id
;选择 binlog_format
:需要根据业务需求选择合适的 binlog 格式(STATEMENT、ROW 或 MIXED)。对于不熟悉 MySQL 的用户来说,这些配置可能会增加使用难度。
总的来说 Mysql 5.7
默认不开启binlog
是因为以下三个方面
- 往往是单机mysql。往往不需要进行数据复制。
- 而且开启bin log 还要一些格外的配置,对不熟悉Mysql 的用户来说,会导致使用难度的增加。
- 性能消耗(在现在来看微乎其微)
MySQL 8.0 及以上版本:binlog 默认是开启的。
可以通过sql 查看是否开启
SHOW VARIABLES LIKE 'log_bin';
- 如果返回结果为
ON
,则表示 binlog 已开启。 - 如果返回结果为
OFF
,则表示 binlog 未开启。
如果 binlog 未开启,你可以通过以下步骤启用它:
修改 MySQL 配置文件
- 找到 MySQL 的配置文件(通常是
my.cnf
或my.ini
)。 - 在
[mysqld]
部分添加或修改以下配置:
[mysqld]
log_bin = /var/lib/mysql/mysql-bin.log # binlog 文件路径
server-id = 1 # 服务器唯一 ID(主从复制时需要)
binlog_format = ROW # 推荐使用 ROW 模式
expire_logs_days = 7 # 设置 binlog 过期时间(单位:天)
- 配置说明:
log_bin
:指定 binlog 文件的路径和名称。server_id
:每个 MySQL 实例需要唯一的 ID(主从复制时必需)。binlog_format
:binlog 的格式,推荐使用ROW
模式(支持更精确的变更数据捕获)。expire_logs_days
:设置 binlog 文件的保留时间,避免磁盘空间被占满。
- 重启服务, 再次验证是否开启
SHOW VARIABLES LIKE 'log_bin';
binlog 的格式
binlog 有三种格式,可以通过 binlog_format
参数设置:
- STATEMENT:
- 记录 SQL 语句。
- 优点:日志文件较小。
- 缺点:某些操作(如非确定性函数)可能导致主从不一致。
- ROW(推荐):
- 记录每一行数据的变更。
- 优点:精确记录数据变更,适用于数据同步和恢复。
- 缺点:日志文件较大。
- MIXED:
- 结合 STATEMENT 和 ROW 模式。
- 默认使用 STATEMENT,在某些情况下自动切换到 ROW。
可以通过以下命令查看当前 binlog 格式:
SHOW VARIABLES LIKE 'log_bin';
MySQL 的Binlog,它记录了所有的 DDL 和 DML语句,也就是记录了数据库中数据的变化。
bin log
的应用
- 数据复制(主从同步)。
- 数据恢复(误操作恢复、灾难恢复)。
- 变更数据捕获(实时数据同步、数据异构)。
- 审计和日志分析
2.Mysql InnoDB和MyISMA 的区别
-
InnoDB和MyISAM的区别
主要的区别: -
1数据的存储结构不一样
- InnoDB:
- 数据和索引存储在同一个文件中(
.ibd
文件)。支持聚簇索引(Clustered Index),索引和值放在一起(二级索引是聚簇索引吗)。
- 数据和索引存储在同一个文件中(
- MyISAM:
- 数据(
.MYD
文件)和索引(.MYI
文件)分开存储。不支持聚簇索引。
- 数据(
tips:数据的存储结构不一样,导致查询效率也不一样,首先,innoDB数据和索引在一个文件中,文件更大查询的较慢,MyISAM只存储索引,所以相同索引下,MyISAM使用的存储空间更少(页更少),查的页数就少,所以MyISAM查的比InnoDB快;其次 聚簇索引的存储方式,可能会发生回表(使用二级索引),而MyISAM不会发生回表,因为他的索引(叶子节点存储的是指向这个数据的物理地址,所以可以一下获得全部的信息,而不用回表)
2. 锁的级别不一样
- InnoDB
- 支持行锁,锁的粒度较低,所以并发能力高,同时行锁的性能开销更大。
- MyISAM
- 支持表锁,锁的粒度较高,所以锁发能力低。
tips:对一行数据修改,InnoDB会使用行锁,MyISAM会使用表锁,表锁的管理简单,只需要维护一个锁对象(整个表)行锁的管理复杂,需要为每一行维护锁信息。
3. 事务支持
- InnoDB支持事务
- redo log (持久性), MVCC 和 锁(隔离性), undo log(原子性), 以上三种特性加上外键构成一致性。
- MyISAM 不支持事务
- 不支持事务,反而没有那么多性能消耗,所以时候读多写少的情况。
总结:
以上特性总结,InnoDB 支持行锁,事务,(外键),索引和数据存放在一起(.idb文件)(可能会回表),能够承受更多的并发,并且发生错误有回顾机制,更加安全。MyISAM,支持表锁,不支持事务,索引和数据单独存放(.myd和.myi),查询效率更高,但是不够安全。
3.Mysql的锁机制
全局锁
对整个数据库加锁,只允许事务的读操作,写操作会处于等待状态
flush tables with read lock; # 加锁
unlock tables; # 解锁,或者推出终端也会解锁
使用场景: 数据备份
如果不加锁,就会出现数据不一致问题。因为在备份的时候,有业务导致数据库在备份的过程中发生变化,导致备份后数据不一致。
如何备份操作呢?
首先进入mysql 终端加锁
flush tables with read lock; # 加锁
新开一个mysql命令行,尝试修改数据,发现修改不了,一直阻塞状态。直到释放全局锁。
然后用mysql提供的工具 mysqldump(主语不是sql语句,直接在我们的电脑终端执行即可)
C:\Users\26611>mysqldump -uroot -p studb > d:/studb.sql
Enter password: ****
缺点: 1. 对整个数据库加锁,数据库只能读,那么业务就不能写,会造成业务停滞。
2.对从库备份,使用全局锁,会导致在使用全局锁时,导致binlog日志文件不能写,会导致延迟。
使用全局锁,会影响业务写操作,那么怎么解决呢?
有些数据库引擎(Innodb)支持可重复读的事务隔离级别,在备份前开启事务会创建 Read View,备份期间业务仍可更新数据。可重复读下,即使其他事务更新,备份的 Read View 不受影响,确保了数据的一致性。使用 mysqldump 备份时,可加 –single-transaction 参数以适应支持此隔离级别的引擎,如 InnoDB。对于 MyISAM 这种不支持事务的引擎,备份时需使用全局锁。
表级锁
对表加锁,表锁,元数据锁,意向锁
表共享锁(Read Lock)的使用
use studb # 选择数据库
lock tables students read; # 对表加共享锁
unlock tables; # 解锁
共享锁,对表加共享锁,允许所有的事务可以读表,但是不能进行写操作;加锁的mysql终端,进行写操作,会报错,其他mysql终端写操作会阻塞。
排他锁(Write Lock) 的使用
use studb # 选择数据库
lock tables students write; # 对标加排他锁
unlock tables; # 解锁
排他锁,对表加排他锁,只有拿到排他锁的事务才可以对标读,写操作,其他事务会阻塞,直到释放锁(unlock tables 或者关闭加锁的终端)。
同时对表加共享锁和排他锁后,加锁的mysql终端不能对其他表操作。如下
mysql> lock tables students read;
Query OK, 0 rows affected (0.00 sec)mysql> select * from test;
ERROR 1100 (HY000): Table 'test' was not locked with LOCK TABLES
元数据锁
元数据锁(MDL)主要用于保护数据库表结构。当一个事务试图修改数据库的结构(如通过`ALTER TABLE`命令添加或删除列)时,它需要先获得相应对象的元数据锁。这种锁确保了在表结构被修改的同时,不会有其他事务尝试访问或修改该表的结构,从而避免数据不一致或错误。MDL加锁过程是系统自动控制,无序显示使用,在访问一张表的时候会自动加上。在表上有活动事务的时候(也就是加上了元数据锁),当一个事务要修改表结构的时候,如果表上加了元数据锁,那么就会进入阻塞状态。
意向锁
意向锁(Intention Lock)主要用于解决行级锁和表级锁之间的冲突,提高数据库的并发性和性能.
意向锁是一种表级别的锁,用于表明当前事务在表中行级锁的特性。它的主要目的是协调行锁和表锁之间的关系,避免锁冲突,从而提高数据库的并发性能。怎么理解呢?
在进行update 操作时会自动加上行级锁,然后其他事务对这个表加表锁,会扫描每一行检查行锁的类型,判断释放能加上表锁。但是有了意向锁之后,进行update 操作(行锁),会在表上加一个意向锁(IS和IX),其他事务在加表锁直接和意向锁比较判断是否能加锁。
- 意向共享锁(IS Lock):
- 当事务打算对某一行加共享锁时,它首先在表级加一个意向共享锁。
- 意向共享锁允许多个事务在表级别并发地读取数据,但不允许修改数据。
- 意向排他锁(IX Lock):
- 当事务打算对某一行加排他锁时,它首先在表级加一个意向排他锁。
- 意向排他锁意味着事务将在行级上加排他锁,其他事务不能在该行上加意向共享锁或意向排他锁。
意向共享锁与共享锁可以兼容,而意向排他锁与排他锁是互斥的。
行锁
- Record Lock(记录锁):单独在记录上加锁,适合如主键记录更新等场景。
举个例子,当一个事务执行了下面这条语句:
mysql > begin; mysql > select * from t_test where id = 1 for update;
就是对 t_test 表中主键 id 为 1 的这条记录加上 X 型的记录锁,这样其他事务就无法对这条记录进行修改了。
当事务执行 commit 后,事务过程中生成的锁都会被释放。
- 当一个事务对一条记录加了 S 型记录锁后,其他事务也可以继续对该记录加 S 型记录锁(S 型与 S 锁兼容),但是不可以对该记录加 X 型记录锁(S 型与 X 锁不兼容);
- 当一个事务对一条记录加了 X 型记录锁后,其他事务既不可以对该记录加 S 型记录锁(S 型与 X 锁不兼容),也不可以对该记录加 X 型记录锁(X 型与 X 锁不兼容)。
-
Gap Lock(间隙锁):Gap Lock 称为间隙锁,只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。假设,表中有一个范围 id 为(3,5)间隙锁,那么其他事务就无法插入 id = 4 这条记录了,这样就有效的防止幻读现象的发生.
-
Next-Key Lock(临键锁):Next-Key Lock 称为临键锁,是 Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。,锁定一个范围,并且锁定记录本身。也不能修改 id = 5 这条记录。所以,next-key lock 即能保护该记录,又能阻止其他事务将新纪录插入到被保护记录前面的间隙中。
4.Mysql 默认的隔离级别为什么是RR
首先,我们先从四种隔离级别中排除Serializable和Read Uncommitted这两种,主要是因为这两个级别一个隔离级别太高,一个太低。太高的就会影响并发度,太低的就有脏读现象。
RR 和 RC 的选择
在MySQL设计之处,他的定位就是提供一个稳定的关系型数据库。而为了要解决MySQL单点故障带来的问题,MySQL采用主从复制的机制。为了保证主从服务器之间的数据的一致性,就需要进行数据同步,大致的同步过程如下,简单理解就是主服务器把数据变更记录到bin log中,然后再把bin log同步传输给从服务器,从服务器接收到bin log之后,再把其中的数据恢复到自己的数据库存储中。
MySQL的bin log主要支持三种格式,分别是statement、row以及mixed。MySQL是在5.1.5版本开始支持row的、在5.1.8版本中开始支持mixed。
- statement: 这是MySQL最早支持的binlog格式。录的是SQL语句的原文。
- 优点:日志记录量相对较小,可以节约磁盘及网络IO,提升性能。 主从版本可以不一样,从服务器版本可以比主服务器版本高。
- 缺点: 可能导致主从同步的数据不一致问题。例如,使用DELETE或UPDATE时指定了LIMIT但没有使用ORDER BY,那么最终这条语句在主库和从库上的执行结果可能不一样。对于包含不确定操作或特定函数的SQL语句(如LOAD_FILE()、UUID()、USER()、FOUND_ROWS()、SYSDATE()等),可能无法被正确复制。
- MySQL从5.1.5版本开始支持row格式的binlog。记录每个数据更改的具体行的细节,即二进制日志中的每个条目都会详细列出发生变更的行的内容和修改。
- 优点: 可以避免MySQL复制中出现主从不一致的问题;对每一行数据的修改比Statement模式高效。在误删改数据后,同时无备份可以恢复时,通过分析binlog日志进行反向处理可以达到恢复数据的目的。
- 缺点:由于需要记录每一行的具体修改,可能导致binlog日志量增大,占用更多存储空间,增加网络传输负担;在复杂的回滚场景中,binlog中会包含大量的数据。
- MySQL从5.1.8版本开始支持mixed格式的binlog,是对Statement和Row两种格式的综合运用。MySQL会根据执行的具体SQL语句选择合适的日志记录方式。对于大多数常规SQL语句,MySQL会选择使用Statement格式记录binlog;当遇到在备库上直接执行原始SQL语句无法达到与主库相同效果的情况(如涉及不确定性的函数、存储过程、触发器等)时,MySQL会自动切换到Row格式,以确保复制的准确性。
因为MySQL早期只有statement这种bin log格式,这时候,如果使用提交读(Read Committed)、未提交读(Read Uncommitted)这两种隔离级别会出现问题
CREATE TABLE t1 (a int(11) DEFAULT NULL,b int(11) DEFAULT NULL,KEY a (a)) ENGINE=InnoDB DEFAULT CHARSET=latin1;insert into t1 values(10,2),(20,1);
数据库记录 (10,2)(20,1)
以上两个事务执行之后,数据库里面的记录会变成(11,2)和(20,2).
以上两个事务执行之后,会在bin log中记录两条记录,因为事务2先提交,所以UPDATE t1 SET b=2 where b=1;
会被优先记录,然后再记录UPDATE t1 SET a=11 where b=2;
(statement格式的bin log记录的是SQL语句的原文)
这样bin log同步到备库之后,SQL语句回放时,会先执行UPDATE t1 SET b=2 where b=1;
,再执行UPDATE t1 SET a=11 where b=2;
。这时候,数据库中的数据就会变成(11,2)和(11,2)。这就导致主库和备库的数据不一致了!!!
为了避免这样的问题发生。MySQL就把数据库的默认隔离级别设置成了Repetable Read,那么,Repetable Read的隔离级别下是如何解决这样问题的那?
设置默认的隔离级别外,MySQL还禁止在使用statement格式的bin log的情况下,使用READ COMMITTED作为事务隔离级别。
一旦用户主动修改隔离级别,尝试更新时,会报错:
选择默认RR级别就是为了兼容历史上的那种statement格式的bin log。
5.Mysql 如何sql调优
平时sql调优主要是通过explain分析慢sql。
可以从以下几个方面考虑
- 建表字段的选择
- 选择合适的字段
- 索引的设计
- 如果经常有多个条件查询尽量建立联合联合索引
- 在经常查询的条件或则排序分组的字段加索引
- 表的修改频率远大于查询频率,考虑是否要建立索引
- 索引不是越多越好,索引需要占用空间,其次增删改操作也会带来索引建立的额外开销,还可能导致页分裂
- sql语句的编写
- 避免 select * ,使用索引覆盖减少回表
- 避免索引失效,遵循最左原则;不使用like%x;不在索引字段操作或则类型转换;范围查询也会导致失效。
- 小表驱动大表
- 架构设计
- 中从复制,读写分离
此外,还可以利用缓存来优化,将一些访问频繁或者变化少的数据设置到缓存,减轻数据库的压力。
6.Mysql 如何分析慢sql
我们通常会使用mysql自动的执行计划explain来去查看这条sql的执行情况。
首先如果本身已经添加了索引,查看key和key_len是否命中了索引,判断索引是否失效,如果失效就排查失效的原因。
其次可以通过type字段查看sql是否有进一步的优化空间,是否存在全索引扫描或全盘扫描。
还可以通过extra建议来判断,是否出现了回表的情况,如果出现了,可以尝试添加索引或修改返回字段来修复。
如下分析的字段
type字段的标识sql的性能(由高到低)
- system : 查系统表
- const :查询的表只有一个匹配结果,通常就是主键或唯一索引查询,并且是常量。
- eq_ref:使用唯一索引查找单个匹配的行。因为索引是唯一的,所以优化器可以确定最多只有一个匹配的行。
- ref :使用非唯一索引或前缀索引来查找单个匹配的行。这通常发生在连接操作中,其中一个表的列与另一个表的索引列相关联。
- range:使用索引来检索给定范围内的行。例如,使用
BETWEEN
、<
、>
、<=
、>=
等条件时。这种访问方式通常比全索引扫描更高效,因为它只扫描索引的一部分。 - index:表示通过遍历整个索引来查找匹配的行。这通常比全表扫描要快,因为索引通常比表数据小。
- all :表示进行全表扫描来找到匹配的行。通常,这种访问方式效率较低,尤其是当表数据量很大时。
7.Mysql 索引的创建如何考虑
索引不是越多越好,索引不仅仅回占用空间,而且每次修改都要维护索引的数据结构消耗资源。所以创建索引要考虑是否要创建。
索引创建的前提是数据量大,数据查询效率低。
以下是创建索引的建议
- 作为常用的查询条件建议添加索引
- order by / group by /distinct 后字段考虑建立索引
- 考虑联合索引,减少回表
- 表的修改频率远大于查询频率,考虑是否要建立索引
8.Mysql使用了索引就一定有效吗?如何排查
不一定有效。
以下场景就会导致索引失效
对于联合索引
- 违反最左匹配原则
- 使用了> < 查询 导致右边的索引失效
对于单个索引
- like %x 的使用
- 对索引进行运算(使用函数)以及类型隐式转化。
- order by 后面不是主键或者不是覆盖索引
- or 两者中要都是索引才能生效
索引失效的原因
总的来说就是在索引构建的时候都是有序的,在使用索引进行查询的时候如果不能和有序的索引结构匹配那就会导致索引失效。
可以使用EXPlAIN 来排查
- 通过key和key_len看是否命中了索引
- 通过type字段看sql是否由优化的空间,避免all,index 全盘扫描
- 通过extra 看是否出现回表,若出现会表可以使用修改返回的所需字段或者使用联合索引。
9.Mysql 回表
回表是指,使用非聚簇索引查询,由于非聚簇索引B+树叶子节点只存储索引值以及主键值,只能查到索引值以及主键值,如果需要其他字段则会根据主键值再去查表这个过程叫做回表。
回表不仅仅在查一次表,还会产生随机IO,因为使用非聚簇索引查到的id可能不唯一并且查到的id是无序的,所以会产生随机IO。
所以要尽量避免回表
- 不使用select * ,明确写出所需的字段
- 索引覆盖
- 索引下推
10.Mysql 为什么选择为使用B+ 树作为数据结构
索引是为了高效获取数据的一种数据结构。
选择B + 树的原因有以下三点
- 查询效率稳定: 相较于二叉树而言,二叉树的最坏情况查询效率是O(N),而B+树是一种自平衡树,每个叶子节点到根节点的距离相同。查找效率O(log(N))。
- 树的高度不会增长过快,磁盘IO少:B+树是多叉树,不像红黑树数据越多高度增长越快;并且相较于B树比较,B+树的非叶子节点只存储页指针和索引值,所以相同页大小可以存更多的索引,使得磁盘IO减少。
- 范围查询快:B+树的叶子节点维护了一个双向链表。B+树在进行范围查询的时候,会根据索引先找到第一个元素,然后根据链表依次获取范围内的元素。
11.Mysql 最左匹配原则
Mysql 的最左匹配原则是指在使用联合索引时,查询条件必须从最左侧匹配,依次向右匹配。违反这个规则就会导致索引失效。
原因:因为联合索引的创建是从左到右有序的创建。
比如,创建(a,b)联合索引,查询条件是where a = 1,;where a = 1 and b = 2;都可以正常使用索引。但是where b = 2 就索引失效。
联合索引的结构
where a > 1, b = 2;
原因:a先经过范围查询 查找到第一个大于a > 2 的元素,然后根据双向链表获得其他a>2的元素,获取的数据b不是有序的,所以要全部遍历判断一下b是否等于2。所以就导致了索引失效。
但是使用 >= , <= 索引 索引不失效
where a>=1,b = 2;
原因: a = 1,b = 2 可以。使用索引查到这个数据,其他的a >1,b = 2 的数据由于在a不相等的情况下,b是无序的,所以还只能走(a)索引,而不是(a,b)索引。
12.Mysql存储引擎
mysql 支持的存储引擎很多可以查看。
show engines
上表的解释
- Engine: 存储引擎的名称。
- Support: 表示该存储引擎是否被MySQL支持(YES)或不支持(NO)。某些存储引擎可能因为特定版本的MySQL或特定的配置而不被支持。
- Comment: 对存储引擎的简短描述,解释了它的基本特性和用途。
- Transactions: 表示该存储引擎是否支持事务(YES)或不支持(NO)。事务是数据库操作的一个逻辑单元,它可以确保一系列操作的原子性、一致性、隔离性和持久性(ACID属性)。
- XA: 表示该存储引擎是否支持XA(eXtended Architecture)事务,这是一种分布式事务的规范,允许跨多个资源(如数据库)执行事务。
- Savepoints: 表示该存储引擎是否支持保存点(YES)或不支持(NO)。保存点允许在事务中设置一个点,可以回滚到这个点而不必回滚整个事务。
主要了解这三个引擎
一、InnoDB
- 特点
- 支持事务:InnoDB提供了具有提交、回滚和崩溃恢复能力的事务安全,即ACID(原子性、一致性、隔离性、持久性)兼容的事务支持。
- 行级锁定:InnoDB支持行级锁定,避免了对整个表或大部分表的加锁,提高了并发性能。
- 索引数据结构: 底层是B+树,快速查找数据,降低磁盘IO(只在叶子节点存储数据),范围查询(叶子节点存储的数据维护一个双向列表)
- 外键约束:InnoDB支持外键约束,确保了数据的完整性和一致性。
- 缓冲池:InnoDB拥有自己的缓冲池,用于在主内存中缓存数据和索引,提高了查询和写入速度。
- 崩溃恢复:InnoDB通过redolog来保证崩溃后的数据恢复,当数据库异常崩溃后,重新启动时会根据redolog进行数据恢复,保证数据库恢复到崩溃前的状态。
- 适用场景
- 适用于需要高事务完整性和并发性能的应用场景,如电子商务网站、金融系统等。
二、MyISAM
- 特点
- 不支持事务:MyISAM不支持事务处理和崩溃恢复功能,因此不适用于需要高事务完整性的应用场景。
- 表级锁定:MyISAM只支持表级锁定,并发性能较差,同时读操作会阻塞写操作,写操作也会阻塞读操作(但读操作之间不会相互阻塞)。
- 索引数据结构:和innodb一样采用了基于B+树的索引机制,但是在叶子节点存储的数据只是索引的值而非整行数据。
- 占用空间较小:MyISAM对数据的压缩和文件大小的管理相对简单,因此在数据管理方面能够占用较小的存储空间。
- 全文索引:MyISAM支持全文索引,适用于需要全文搜索的应用场景。
- 适用场景
- 适用于读操作远远多于写操作的场景,如数据仓库、日志记录等。
三、MEMORY
- 特点
- 数据存储在内存中:MEMORY存储引擎将数据存储在内存中,因此读写速度非常快。
- 不支持持久化存储:当MySQL服务器关闭或重启时,MEMORY引擎中的数据将丢失,因此不适用于需要长期保存数据的应用场景。
- 支持哈希索引:可以快速查询数据,但是不能范围查询。
- 表级锁定:MEMORY引擎使用表级锁定,可能导致并发性能问题。
- 受限于可用内存大小:由于数据存储在内存中,因此受限于可用的内存大小,如果表过大,可能无法完全缓存在内存中,导致性能下降。
- 适用场景
- 适用于临时表、缓存表和高性能临时存储,如缓存数据等。
13.Mysql索引失效的场景以及原理
有以下表
CREATE TABLE `user` (`id` int NOT NULL AUTO_INCREMENT,`username` varchar(255) NOT NULL,`age` int NOT NULL,`phone` varchar(50) NOT NULL,PRIMARY KEY (`id`),KEY `idx_username` (`username`),KEY `idx_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
主键索引 id
索引的叶子节点存整行的数据。
二级索引 username
二级索引的叶子节点存的值是主键的值。若使用二级索引查找,就只能找到该索引本身的值以及该数据的主键的值,若需要该行数据的其他数据,就会根据获得的主键值再去查找,也叫回表。
索引失效的场景
- 使用模糊匹配
select * from user where username like '%李'
select * from user where username like '%李%'
select * from user where username like '李%' # 这个可以正常使用索引
可以使用explain 分析以上sql
为什么使用左模糊匹配会导致索引失效呢?
结合索引的结构就很容易想明白。索引的构建是先根据索引字段的值排序的,对于字符类型是字典序排序。要首先判断前面的字,才能够有序查找,而左模糊匹配找的是最后一个字匹配,索引就不能够使用索引找,而是全局扫描了一遍。
- 对索引字使用函数
select *from user where length(username) = 6;
失效的原因是:索引保存的索引字段的初始值,而不是经过函数计算后的值,自然就没法走索引。
- 对索引进行表达式计算
select * from user where id + 1 = 10;
失效的原因:索引保存的索引字段的值,而不是id+1后的值,所以无法走索引。
- 对索引隐式的类型转换
# phone 是 vachar类型
select * from user where phone = 123456789; # 不走索引
select * from user where phone = '123456789'; # 走索引
失效的原因,mysql看到参数是整形数字,将phone改为int 类型。相当于
select * from user where CAST(phone AS signed int) = 123456789
索引构建存的是字符串类型,查询的时候将索引的类型变了。
- 联合索引违反最左匹配原则联合索引,索引的构建是先根据左边的索引排序,在根据右边的索引排序。比如,创建(a,b)联合索引,查询条件是where a = 1,;where a = 1 and b = 2;都可以正常使用索引。但是where b = 2 就索引失效。联合索引的结构
失效的原理:索引的构建是从左到右排序构建索引,不按照最左匹配原则查询,查询的条件就是无序了,也就无法走索引。
总的来说,索引失效的原理,都是通过条件无法二分查询值,就只能走全表扫描。判断索引是否失效不仅仅要要求我们编写sql的时候注意导致索引失效的sql的编写,还要使用explain查看sql的执行情况。
14.Mysql 三层B+树能存多少数据
要计算三层B+能存储多少数据,要清楚几个数据,数据页大小,索引值大小,指针大小,数据行大小。
Mysql数据存储的基本单位是页。默认大小是16kb。
show global status like 'innodb_page_size';
Variable_name | Value |
---|---|
innodb_page_size | 16384 |
- 16KB = 16 * 1024 = 16384 字节
假设 数据行大小为 1Kb,索引大小8字节(bigint)指针6字节。
第一层 B+ 树 只存储指针和索引值所以第一层的结点 16384 / (8 + 6) = 1170
第二层 B+ 树 在第一层1170 的节点上再分 即 1170 *1170 = 1,368,900
第三层 B+ 树 存储数据行,1368900*16kb / 1kb = 21,902,400
大概可以存2000w条数据。
数据结构大概这样
15.Mysql的索引类型
按照字段特性划分
- 主键索引 :修饰的字段唯一且为非null,通常是数据唯一的标识,同时在为主键构建索引时,索引结构的叶子结点存储整行的数据,查询效率较高。
- 唯一索引:修饰的字段唯一性约束可以为null。
- 普通索引:非主键索引非唯一索引。
- 联合索引:多个列共同组成的索引,适用于多个条件查询提升查询效率,联合索引的构建是有顺序的,索引查询的时候要遵循最左匹配原则,同时联合索引也适用于索引覆盖。
按照索引结构存储数据来分:
- 聚簇索引 : 索引的叶子节点存储整行信息。
- 非聚簇索引 : 叶子节点只存储索引值信息。
按照索引数据结构来分
- B+树索引:是MySQL中一种常见的索引类型,适用于范围查询、排序操作和等值查询。InnoDB和MyISAM存储引擎都支持B树索引。
- Hash索引: 基于哈希表的数据结构,适用于精确匹配查询,但不支持范围查询或部分匹配查询。MEMORY存储引擎默认使用哈希索引,而InnoDB不直接支持,但可以通过覆盖索引来实现类似效果。
- 倒排索引 :基于倒排索引的数据结构,专门用于全文搜索,适用于需要进行复杂文本匹配的操作,如文章内容、评论等。InnoDB和MyISAM存储引擎都支持全文索引。
16.聚簇索引和非聚簇索引的区别
聚簇索引:索引数据结构的叶子节点存储整行的信息,在一个表中只有一个聚簇索引,一般是主键/唯一键/隐藏row_id。
非聚簇索引:索引数据结构的叶子节点只存储主键值以及索引值。使用非聚簇索引查询的时候要注意回表,尽量避免select * 这种查询,并且添加联合索引,使得查询的列能在叶子节点全部获取。
补充:
**回表**:回表和聚簇索引和非聚簇索引是有关系的,回表的意思就是通过二级索引找到对应的主键值,然后再通过主键值找到聚集索引中所对应的整行数据,这个过程就是回表。**索引覆盖**:覆盖索引是指select查询语句使用了索引,在返回的列,必须在索引中全部能够找到,如果我们使用id查询,它会直接走聚集索引查询,一次索引扫描,直接返回数据,性能高。
如果按照二级索引查询数据的时候,返回的列中没有创建索引,有可能会触发回表查询,尽量避免使用select *,尽量在返回的列中都包含添加索引的字段。
17.MVCC 是什么?
MVCC 的意思是多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突。它的底层实现主要是分为了三个部分,第一个是隐藏字段,第二个是undo log日志,第三个是readView读视图。
隐藏字段
- DB_TRX_ID :最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID。
- DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本。
- DB_ROW_ID:隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。
undo log 日志
回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志。
当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。
而update、delete的时候,产生的undo log日志不仅在回滚时需要,mvcc版本访问也需要,不会立即被删除。
在多个事务对一条记录修改的情况况下,undolog日志记录了版本链。
ReadView 读视图
ReadView(读视图)是 快照读 SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
- 当前读 :读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select … lock in share mode(共享锁),select … for update、update、insert、delete(排他锁)都是一种当前读。
- 快照读:简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
ReadView的四个核心字段
- m_ids : 当前活跃的事务ID集合
- min_trx_id : 最小活跃事务ID
- max_trx_id : 预分配事务ID,当前最大事务ID+1(因为事务ID是自增的)
- creator_trx_id : ReadView创建者的事务ID
版本链数据的访问规则
不同的隔离级别,产生ReadView的时机不同。
- Read Committed:每次select,都生成一个快照读。
- Repeatable Read:开启事务后第一个select语句才是快照读的地方。