04-MySQL02

1、什么是索引下推?

索引下推(index condition pushdown )简称ICP,在Mysql5.6的版本上推出,用于优化查询。

需求: 查询users表中 "名字第一个字是张,年龄为10岁的所有记录"。

SELECT * FROM users WHERE user_name LIKE '张%' AND user_age = 10;

根据最左前缀法则,该语句在搜索索引树的时候,只能匹配到名字第一个字是‘张’的记录,接下来是怎么处理的呢?当然就是从该记录开始,逐个回表,到主键索引上找出相应的记录,再比对 `age` 这个字段的值是否符合。

在 (name,age) 索引里面特意去掉了 age 的值,这个过程 InnoDB 并不会去看 age 的值,只是按顺序把“name 第一个字是’张’”的记录一条条取出来回表。因此,需要回表 4 次。

04-MySQL02_第1张图片

MySQL 5.6引入了索引下推优化,可以在索引遍历过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录,减少回表次数。

InnoDB 在 (name,age) 索引内部就判断了 age 是否等于 10,对于不等于 10 的记录,直接判断并跳过,减少回表次数.
04-MySQL02_第2张图片

总结

如果没有索引下推优化(或称ICP优化),当进行索引查询时,首先根据索引来查找记录,然后再根据where条件来过滤记录;

在支持ICP优化后,MySQL会在取出索引的同时,判断是否可以进行where条件过滤再进行索引查询,也就是说提前执行where的部分过滤操作,在某些场景下,可以大大减少回表次数,从而提升整体性能。

2、什么是自适应哈希索引?

自适应Hash索引(Adatptive Hash Index,内部简称AHI)是InnoDB的三大特性之一,还有两个是 Buffer Pool简称BP、双写缓冲区(Doublewrite Buffer)。

1、自适应即我们不需要自己处理,当InnoDB引擎根据查询统计发现某一查询满足hash索引的数据结构特点,就会给其建立一个hash索引;

2、hash索引底层的数据结构是散列表(Hash表),其数据特点就是比较适合在内存中使用,自适应Hash索引存在于InnoDB架构中的缓存中(不存在于磁盘架构中),见下面的InnoDB架构图。

3、自适应hash索引只适合搜索等值的查询,如select * from table where index_col='xxx',而对于其他查找类型,如范围查找,是不能使用的;

当我们在进行等值查询的时候,他会在下面维护一张Hash表。和B+树的叶子节点进行一个关联,这样我们在进行范围查找的时候就会比较快。

04-MySQL02_第3张图片

InnoDB的自适应Hash索引是默认开启的,可以通过配置下面的参数设置进行关闭。

innodb_adaptive_hash_index = off

3、为什么LIKE以%开头索引会失效?

like查询为范围查询,%出现在左边,则索引失效。%出现在右边索引未失效.

场景1: 两边都有% 或者 字段左边有%,索引都会失效。

EXPLAIN SELECT * FROM users WHERE user_name LIKE '%tom%';

EXPLAIN SELECT * FROM users WHERE user_name LIKE '%tom';

场景2: 字段右边有%,索引生效

EXPLAIN SELECT * FROM users WHERE user_name LIKE 'tom%';

解决%出现在左边索引失效的方法,使用覆盖索引

EXPLAIN SELECT user_name FROM users WHERE user_name LIKE '%jack%';

EXPLAIN SELECT user_name,user_age,user_level FROM users WHERE user_name LIKE '%jack%';

对比场景1可以知道, 通过使用覆盖索引 `type = index`,并且 `extra = Using index`,从全表扫描变成了全索引扫描.

总结like 失效的原因:

1. %号在右: 由于B+树的索引顺序,是按照首字母的大小进行排序,%号在右的匹配首字母。所以可以在B+树上进行有序的查找,查找首字母符合要求的数据。
2. %号在左:  是匹配字符串尾部的数据,我们上面说了排序规则,尾部的字母是没有顺序的,所以不能按照索引顺序查询,就用不到索引.
3. 两个%%号:  这个是查询任意位置的字母满足条件即可,只有首字母是进行索引排序的,其他位置的字母都是相对无序的,所以查找任意位置的字母是用不上索引的.

4、自增还是UUID?数据库主键的类型该如何选择?

auto_increment的优点:

1. 字段长度较uuid小很多,可以是bigint甚至是int类型,这对检索的性能会有所影响。
2. 在写的方面,因为是自增的,所以主键是趋势自增的,也就是说新增的数据永远在后面,这点对于性能有很大的提升。
3. 数据库自动编号,速度快,而且是增量增长,按顺序存放,对于检索非常有利。
4. 数字型,占用空间小,易排序,在程序中传递也方便。

auto_increment的缺点:

1. 由于是自增,很容易通过网络爬虫知晓当前系统的业务量。
2. 高并发的情况下,竞争自增锁会降低数据库的吞吐能力。
3. 数据迁移或分库分表场景下,自增方式不再适用。

UUID的优点:

1. 不会冲突。进行数据拆分、合并存储的时候,能保证主键全局的唯一性
2. 可以在应用层生成,提高数据库吞吐能力

UUID的缺点:

1. 影响插入速度, 并且造成硬盘使用率低。与自增相比,最大的缺陷就是随机io,下面我们会去具体解释
2. 字符串类型相比整数类型肯定更消耗空间,而且会比整数类型操作慢。

使用自增 id 的内部结构:

自增的主键的值是顺序的,所以 InnoDB 把每一条记录都存储在一条记录的后面。

* 当达到页面的最大填充因子时候(InnoDB 默认的最大填充因子是页大小的 15/16,会留出 1/16 的空间留作以后的修改)。
* 下一条记录就会写入新的页中,一旦数据按照这种顺序的方式加载,主键页就会近乎于顺序的记录填满,提升了页面的最大填充率,不会有页的浪费。
* 新插入的行一定会在原有的最大数据行下一行,MySQL 定位和寻址很快,不会为计算新行的位置而做出额外的消耗。减少了页分裂和碎片的产生。

使用 uuid 的索引内部结构

插入UUID: 新的记录可能会插入之前记录的中间,因此需要移动之前的记录

因为 uuid 相对顺序的自增 id 来说是毫无规律可言的,新行的值不一定要比之前的主键的值要大,所以 innodb 无法做到总是把新行插入到索引的最后,而是需要为新行寻找新的合适的位置从而来分配新的空间。

这个过程需要做很多额外的操作,数据的毫无顺序会导致数据分布散乱,将会导致以下的问题:

1. 写入的目标页很可能已经刷新到磁盘上并且从缓存上移除,或者还没有被加载到缓存中,innodb 在插入之前不得不先找到并从磁盘读取目标页到内存中,这将导致大量的随机 IO。
2. 因为写入是乱序的,innodb 不得不频繁的做页分裂操作,以便为新的行分配空间,页分裂导致移动大量的数据,一次插入最少需要修改三个页以上。
3. 由于频繁的页分裂,页会变得稀疏并被不规则的填充,最终会导致数据会有碎片。
4. 在把随机值(uuid 和雪花 id)载入到聚簇索引(InnoDB 默认的索引类型)以后,有时候会需要做一次 OPTIMEIZE TABLE 来重建表并优化页的填充,这将又需要一定的时间消耗。

结论:使用 InnoDB 应该尽可能的按主键的自增顺序插入,并且尽可能使用单调的增加的聚簇键的值来插入新行。如果是分库分表场景下,分布式主键ID的生成方案 优先选择雪花算法生成全局唯一主键(雪花算法生成的主键在一定程度上是有序的)。

5、InnoDB与MyISAM的区别?

InnoDB和MyISAM是使用MySQL时最常用的两种引擎类型,我们重点来看下两者区别。

* 事务和外键
  InnoDB支持事务和外键,具有安全性和完整性,适合大量insert或update操作
  MyISAM不支持事务和外键,它提供高速存储和检索,适合大量的select查询操作
* 锁机制
  InnoDB支持行级锁,锁定指定记录。基于索引来加锁实现。
  MyISAM支持表级锁,锁定整张表。
* 索引结构
  InnoDB使用聚簇索引,索引和记录在一起存储,既缓存索引,也缓存记录。
  MyISAM使用非聚簇索引,索引和记录分开。
* 并发处理能力
  MyISAM使用表锁,会导致写操作并发率低,读之间并不阻塞,读写阻塞。
  InnoDB读写阻塞可以与隔离级别有关,可以采用多版本并发控制(MVCC)来支持高并发
* 存储文件
  InnoDB表对应两个文件,一个.frm表结构文件,一个.ibd数据文件。InnoDB表最大支持64TB;
  MyISAM表对应三个文件,一个.frm表结构文件,一个MYD表数据文件,一个.MYI索引文件。从MySQL5.0开始默认限制是256TB。

MyISAM 适用场景:

* 不需要事务支持(不支持)
* 并发相对较低(锁定机制问题)
* 数据修改相对较少,以读为主
* 数据一致性要求不高

InnoDB 适用场景

* 需要事务支持(具有较好的事务特性)
* 行级锁定对高并发有很好的适应能力
* 数据更新较为频繁的场景
* 数据一致性要求较高
* 硬件设备内存较大,可以利用InnoDB较好的缓存能力来提高内存利用率,减少磁盘IO

两种引擎该如何选择?

* 是否需要事务?有,InnoDB
* 是否存在并发修改?有,InnoDB
* 是否追求快速查询,且数据修改少?是,MyISAM
* 在绝大多数情况下,推荐使用InnoDB

6、Mysql内部支持缓存查询吗?

使用缓存的好处:当MySQL接收到客户端的查询SQL之后,仅仅只需要对其进行相应的权限验证之后,就会通过Query Cache来查找结果,甚至都不需要经过Optimizer模块进行执行计划的分析优化,更不需要发生任何存储引擎的交互.

mysql5.7支持内部缓存,8.0之后已废弃

mysql缓存的限制

1. mysql基本没有手段灵活的管理缓存失效和生效,尤其对于频繁更新的表
2. SQL必须完全一致才会导致cache命中
3. 为了节省内存空间,太大的result set不会被cache (< query_cache_limit);
4. MySQL缓存在分库分表环境下是不起作用的;
5. 执行SQL里有触发器,自定义函数时,MySQL缓存也是不起作用的;
6. 在表的结构或数据发生改变时,基于该表相关cache立即全部失效。

替代方案

* 应用层组织缓存,最简单的是使用redis,ehcached等

7、mysql 线上修改大表结构有哪些风险?

在线修改大表的可能影响

* 在线修改大表的表结构执行时间往往不可预估,一般时间较长。
* 由于修改表结构是表级锁,因此在修改表结构时,影响表写入操作。
* 如果长时间的修改表结构,中途修改失败,由于修改表结构是一个事务,因此失败后会还原表结构,在这个过程中表都是锁着不可写入。
* 修改大表结构容易导致数据库CPU、IO等性能消耗,使MySQL服务器性能降低。
* 在线修改大表结构容易导致主从延时,从而影响业务读取。

修改方式:

1. 对表加锁(表此时只读)
2. 复制原表物理结构
3. 修改表的物理结构
4. 把原表数据导入中间表中 ,数据同步完后,锁定中间表,并删除原表
5. rename中间表为原表
6. 刷新数据字典,并释放锁

使用工具: pt-online-schema-change ,是percona推出的一个针对mysql在线ddl的工具。percona是一个mysql分支维护公司,专门提供mysql技术服务的。

8、MySQL的binlog有几种日志格式?分别有什么区别?

binlog日志有三种模式

1)ROW(row-based replication, RBR):日志中会记录每一行数据被修改的情况,然后在slave端对相同的数据进行修改。

* 优点:能清楚记录每一个行数据的修改细节,能完全实现主从数据同步和数据的恢复。而且不会出现某些特定情况下存储过程或function无法被正确复制的问题。
* 缺点:批量操作,会产生大量的日志,尤其是alter table会让日志量暴涨。

2)STATMENT(statement-based replication, SBR):记录每一条修改数据的SQL语句(批量修改时,记录的不是单条SQL语句,而是批量修改的SQL语句事件), slave在复制的时候SQL进程会解析成和原来master端执行过的相同的SQL再次执行。简称SQL语句复制。

* 优点:日志量小,减少磁盘IO,提升存储和恢复速度
* 缺点:在某些情况下会导致主从数据不一致,比如last_insert_id()、now()等函数。

04-MySQL02_第4张图片

3)MIXED(mixed-based replication, MBR):以上两种模式的混合使用,一般会使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog,MySQL会根据执行的SQL语句选择写入模式。

企业场景如何选择binlog的模式

1. 如果生产中使用MySQL的特殊功能相对少(存储过程、触发器、函数)。选择默认的语句模式,Statement。
2. 如果生产中使用MySQL的特殊功能较多的,可以选择Mixed模式。
3. 如果生产中使用MySQL的特殊功能较多,又希望数据最大化一致,此时最好Row 模式;但是要注意,该模式的binlog日志量增长非常快.

9、bin log与redo log的区别?

1)redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。

2)redo log是物理日志,记录的是“在XXX数据页上做了XXX修改”;binlog是逻辑日志,记录的是原始逻辑,其记录是对应的SQL语句。

3)redo log是循环写的,空间一定会用完,需要write pos和check point搭配;binlog是追加写,写到一定大小会切换到下一个,并不会覆盖以前的日志。

* Redo Log 文件内容是以顺序循环的方式写入文件,写满时则回溯到第一个文件,进行覆盖写。

4)Redo Log作为服务器异常宕机后  事务数据自动恢复使用,Binlog可以作为主从复制和数据恢复使用。Binlog没有自动crash-safe能力

CrashSafe指MySQL服务器宕机重启后,能够保证:

* 所有已经提交的事务的数据仍然存在。
* 所有没有提交的事务的数据自动回滚。

10、说说 MySQL 的主从复制?

1、主从复制的用途

- 实时灾备,用于故障切换
- 读写分离,提供查询服务
- 备份,避免影响业务

2、主从部署必要条件

- 主库开启binlog日志(设置log-bin参数)
- 主从server-id不同
- 从库服务器能连通主库

3、主从复制的原理

- Mysql 中有一种日志叫做 binlog 日志(二进制日志)。这个日志会记录下所有修改了数据库的SQL 语句(insert,update,delete,create/alter/drop table, grant 等等)。
- 主从复制的原理其实就是把主服务器上的 binlog日志复制到从服务器上执行一遍,这样从服务器上的数据就和主服务器上的数据相同了。
04-MySQL02_第5张图片

1. 主库db的更新事件(update、insert、delete)被写到binlog。
2. 主库创建一个binlog dump thread,把binlog的内容发送到从库。
3. 从库启动并发起连接,连接到主库。
4. 从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log。
5. 从库启动之后,创建一个SQL线程,从relay log里面读取内容,执行读取到的更新事件,将更新内容写入到slave的db。


11、redo log与undo log的持久化策略?

11.1、redo log持久化

缓冲区数据一般情况下是无法直接写入磁盘的,中间必须经过操作系统缓冲区( OS Buffer )。因此, redo log buffer 写入 redo logfile 实际上是先写入 OS Buffer,然后再通过系统调用 fsync() 将其刷到 redo log file.

Redo Buffer 持久化到 redo log 的策略,可通过 `Innodb_flush_log_at_trx_commit` 设置:

0 (延迟写) :事务提交时不会将 `redo log buffer`中日志写入到 `os buffer`, 而是每秒写入 `os buffer`并调用 `fsync()`写入到 `redo log file`中。 也就是说设置为0时是(大约)每秒刷新写入到磁盘中的,当系统崩溃,会丢失1秒钟的数据。

1  (实时写,实时刷):事务每次提交都会将 `redo log buffer`中的日志写入 `os buffer`并 调用 `fsync()`刷到 `redo log file`中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO的性能较差。

2 (实时写, 延时刷) :每次提交都仅写入到 `os buffer`,然后是每秒调用 `fsync()`将 `os buffer`中的日志写入到 `redo log file`。

04-MySQL02_第6张图片

一般建议选择取值2,因为 MySQL 挂了数据没有损失,整个服务器挂了才会损失1秒的事务提交数据。

11.2、undo log持久化

MySQL中的Undo Log严格的讲不是Log,而是数据,因此他的管理和落盘都跟数据是一样的。

你可能感兴趣的:(08-面试,数据库,mysql)