
Q1:MySQL 的逻辑架构了解吗?
一个事务在逻辑上是必须不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说不可能只执行其中的一部分。
数据库总是从一个一致性的状态转换到另一个一致性的状态。
针对并发事务而言,隔离性就是要隔离并发运行的多个事务之间的相互影响,一般来说一个事务所做的修改在最终提交以前,对其他事务是不可见的。
一旦事务提交成功,其修改就会永久保存到数据库中,此时即使系统崩溃,修改的数据也不会丢失。
在该级别事务中的修改即使没有被提交,对其他事务也是可见的,事务可以读取其他事务修改完但未提交的数据,这种问题称为脏读。这个级别还会导致不可重复读和幻读,性能没有比其他级别好很多,很少使用。
提交读 READ COMMITTED
多数数据库系统默认的隔离级别。提交读满足了隔离性的简单定义:一个事务开始时只能"看见"已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前的任何修改对其他事务都是不可见的。也叫不可重复读,因为两次执行同样的查询可能会得到不同结果。
可重复读 REPEATABLE READ(MySQL默认的隔离级别)
可重复读解决了不可重复读的问题,保证了在同一个事务中多次读取同样的记录结果一致。但还是无法解决幻读,所谓幻读指的是当某个事务在读取某个范围内的记录时,会产生幻行。InnoDB存储引整通过多版本并发控制MVCC 解决幻读的问题。
可串行化SERIALIZABLE
最高的隔离级别,通过强制事务串行执行,避免幻读。可串行化会在读取的每一行数据上都加锁,可能导致大量的超时和锁争用的问题。实际应用中很少用到这个隔离级别,只有非常需要确保数据一致性且可以接受没有并发的情况下才考虑该级别。
InnoDB 的MVCC 通过在每行记录后面保存两个隐藏的列来实现,这两个列一个保存了行的创建时间,一个保存行的过期时间间。不过存储的不是实际的时间值而是系统版本号,每开始一个新的事务系统版本号都会自动递增,事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
MVCC 只能在READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作,因为 READ
UNCOMMITTED 总是读取最新的数据行,而不是符合当前事务版本的数据行,而 SERIALIZABLE 则会对所有读取的行都加锁。
一般情况下尽量使用可以正确存储数据的最小数据类型,更小的数据类型通常也更快,因为它们占用更少的磁盘、内存和 CPU 缓存。
通常情况下最好指定列为 NOT NULL,除非需要存储 NULL值。因为如果查询中包含可为 NULL 的列对MySQL 来说更难优化,可为 NULL 的列使索引、索引统计和值比较都更复杂,并且会使用更多存储空间。当可为 NULL 的列被索引时,每个索引记录需要一个额外字节,在MyISAM 中还可能导致固定大小的索引变成可变大小的索引。
如果不是按照索引的最左列开始查找,则无法使用索引。
不能跳过索引中的列,例如索引为 (id.name.sex),不能只使用 id和 sex 而跳过name.
如果查询中有某个列的范围查询,则其右边的所有列都无法使用索引。
有一些限制:
哈希索引数据不是按照索引值顺序存储的,无法用于排序。
的。例如在数据列(a,b)上建立哈希索引,如果查询的列只有a就无法使用该索引。
通过数值比较、范围过源等就可以完成绝大多数需要的查询,但如果希望通过关键字匹配进行查询,就需要基于相似度的查询,而不是精确的数值比较,全文索引就是为这种场景设计的,
MyISAM 的全文索引是一种特殊的 B-Tree 索引,一共有两层,第一层是所有关键字,然后对于每一个关键字的第二层,包含的是一组相关的"文档指针"。全文索引不会索引文档对象中的所有词语。它会根据规则过滤掉一些词语,例如信用词列表中的词都不会被索引。
Q22:什么是聚簇索引?
聚簇索引不是一种索引类型,而是一种数据存储方式。InnoDB的聚簇索引实际上在同一个结构中保存了 B-Tree 索引和数据行。当表有聚餐索引时,它的行数据实际上存放在索引的叶子页中,因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
优点:① 可以把相关数据保存在一起。② 数据访问更快,聚簇索引将索引和数据保存在同一个 B-Tree中,因此获取数据比非聚簇索引要更快。③ 使用覆盖索引扫描的查询可以直接使用页节点中的主键值。
缺点:① 聚簇索引最大限度提高了 10 密集型应用的性能,如果数据全部在内存中将会失去优势。② 更新聚簇索引列的代价很高,因为会强制每个被更新的行移动到新位置。③ 基于聚簇索引的表插入新行或主键被更新导致行移动时,可能导致页分裂,表会占用更多磁盘空间。4 当行稀疏或由于页分裂导致数据存储不连续时,全表扫描可能很慢。
Q23:什么是覆盖索引?
覆盖索引指一个索引包含或覆盖了所有需要查询的字段的值,不再需要根据索引回表查询数据。覆盖索引必须要存储索引列的值,因此 MySQL 只能使用 B-Tree 索引做覆盖索引。
优点:①索引条目通常远小于数据行大小,可以极大减少数据访问量。2 因为索引按照列值顺序存
储,所以对于 I0 密集型防伪查询回避随机从磁盘读取每一行数据的 10 少得多。③ 由于 InnoDB 使用聚簇索引,覆盖索引对 InnoDB 很有帮助。InnoDB 的二级索引在叶子节点保存了行的主键值,如果二级主键能覆盖查询那么可以避免对主键索引的二次查询。
建立索引
对查询频次较高且数据量比较大的表建立索引,索引字段的选择,最佳候选列应当从 WHERE 子句的条件中提取,如果 WHERE 子句中的组合比较多,应当挑选最常用、过滤效果最好的列的组合,业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。
使用前缀索引
选择合适的索引顺序
删除无用索引
如果索引列出现了隐式类型转换,则 MySQL 不会使用索引。常见的情况是在 SOL 的 WHERE 条件中字段类型为字符串,其值为数值,如果没有加引号那么 MySOL不会使用索引。
索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
Q26:如何定位低效 SQL?
可以通过两种方式来定位执行效率较低的 SQL 语句。一种是通过慢查询日志定位,可以通过慢查询日志定位那些已经执行完毕的 SOL 语句。另一种是使用 SHOW PROCESSLIST 查询,慢查询日志在查询结束以后才记录,所以在应用反应执行效率出现问题的时候查询慢查询日志不能定位问题,此时可以使用SHOW PROCESSLIST 命令查看当前 MySOL 正在进行的线程,包括线程的状态,是否锁表等,可以实时查看 SOL的执行情况,同时对一些锁表操作进行优化。找到执行效率低的 SOL 语句后,就可以通过SHOW PROFILE、EXPLAIN 或 trace 等丰富来继续优化语句。
通过 SHOW PROFILE 可以分析 SQL 语句性能消耗,例如查询到 SQL会执行多少时间,并显示 CPU、内存使用量,执行过程中系统锁及表锁的花费时间等信息。例如 SHOW PROFILE CPU/MEMORY/BLOCK IO FOR QUERY N 分别查询id为N的 SQL 语句的 CPU、内存以及 IO 的消耗情况。
Q28: trace 是干什么的?
从 MySQL5.6 开始,可以通过 trace 文件进一步获取优化器是是如何选择执行计划的,在使用时需要先打开设置,然后执行一次SQL,最后查看 information schema.optimizer trace 表而都内容,该长为联合i表,只能在当前会话进行查询,每次查询后返回的都是最近一次执行的 SQL 语句。
Q29:EXPLAIN 的字段有哪些,具有什么含义?
执行计划是 SOL调优的一个重要依据,可以通过 EXPLAIN 命令查看 SOL 语句的执行计划,如果作用在表上,那么该命令相当于 DESC。EXPLAIN 的指标及含义如下:

Q30:有哪些优化 SQL 的策略?
优化 COUNT 查询
COUNT 是一个特殊的的数,它可以统计某个列值的数量,在统计列值时要求列值是非空的,不会统计NULL 值。如果在 COUNT 中指定了列或列的表达式,则统计的就是这个表达式有值的结果数,而不是NULL。
COUNT 的另一个作用是统计结果集的行数,当 MySOL 确定括号内的表达式不可能为 NULL 时,实际上就是在统计行数。当使用 COUNT(*)时,*不会扩展成所有列,它会忽略所有的列而直接统计所有的行数。
某些业务场景并不要求完全精确的 COUNT值,此时可以使用近似值来代替,EXPLAIN 出来的优化器估算的行数就是一个不错的近似值,因为执行 EXPLAIN 并不需要真正地执行查询。
通常来说 COUNT 都需要扫描大量的行才能获取精确的结果,因此很难优化。在 MySOL层还能做的就只有覆盖扫描了,如果还不够就需要修改应用的架构,可以增加汇总表或者外部缓存系统。
优化关联查询
确保 ON 或 USING 子句中的列上有索引,在创建索引时就要考虑到关联的顺序。
确保任何 GROUP BY 和 ORDER BY 的表达式只涉及到一个表中的列,这样 MySQL才有可能使用索引来优化这个过程。
在 MySQL 5.5 及以下版本尽量避免子查询,可以用关联查询代替,因为执行器会先执行外部的 SQL 再执行内部的 SQL。
优化 GROUP BY
如果没有通过ORDER BY 子句显式指定要排席的列,当查询使用 GROUP BY 时,结果***自动按照分组的字段进行排序,如果不关心结果集的顺序,可以使用 ORDER BY NULL 禁止排序。
优化 LIMIT 分页
在偏移量非常大的时候,需要查询很多条数据再舍弃,这样的代价非常高。要优化这种查询,要么是在页面中限制分页的数量,要么是优化大偏移量的性能。最简单的办法是尽可能地使用覆盖索引扫描,而不是查询所有的列,然后根据需要做一次关联操作再返回所需的列。
还有一种方法是从上一次取数据的位置开始扫描,这样就可以避免使用 OFFSET。其他优化方法还包括使用预先计算的汇总表,或者关联到一个冗余表,冗余表只包含主键列和需要做排序的数据列。
优化 UNION 查询
MySQL 通过创建并填充临时表的方式来执行 UNION 查询,除非确实需要服务器消除重复的行,否则一定要使用UNION ALL,如果没有 ALL关键字,MVSQL 会给临时表加上 DISTINCT选项,这会导致对整个临时表的数据做唯一性检查,这样做的代价非常高。
使用用户自定义变量
在查询中混合使用过程化和关系化逻辑的时候,自定义变量可能会非常有用。用户自定义变量是一个用来存储内容的临时容器,在连接 MySQL 的整个过程中都存在,可以在任何可以使用表达式的地方使用自定义变量,例如可以使用变量来避免重复查询刚刚更新过的数据,统计更新和插入的数量等。
优化 INSERT
需要对一张表插入很多行数据时,应该尽量使用一次性插入多个值的 INSERT 语句,这种方式将缩减客户端与数据库之间的连接、关闭等消耗,效率比多条插入单个值的 INSERT 语句高,也可以关闭事务的自动提交,在插入完数据后提交。当插入的数据是按主键的顺序插入时,效率更高。
Q31:MySQL 主从复制的作用?
复制解决的基本问题是让一台服务器的数据与其他服务器保持同步,一台主库的数据可以同步到多台备库上,备库本身也可以被配置成另外一台服务器的主库,主库和备库之间可以有多种不同的组合方式。
MySQL 支持两种复制方式:基于行的复制和基于语句的复制,基于语句的复制也称为逻辑复制,从
MySQL 3.23 版本就已存在,基于行的复制方式在 5.1 版本才被加进来。这两种方式都是通过在主库上记录二进制目志、在备库重放日志的方式来实现异步的数据复制。因此同一时刻备库的数据可能与主库存在不一致,并且无法包装主备之间的延迟。
MySQL 复制大部分是向后兼容的,新版本的服务器可以作为老版本服务器的备库,但是老版本不能作为新版本服务器的备库,因为它可能无法解析新版本所用的新特性或语法,另外所使用的二进制文件格式也可能不同。
复制解决的问题:数据分布、负载均衡、备份、高可用性和故障切换、MySQL 升级测试。
Q32: MySQL 主从复制的步骤?
①在主库上把数据更改记录到二进制日志中。(2)备库将主库的日志复制到自己的中继日志中。(3 备库读取中继日志中的事件,将其重放到备库数据之上。
第一步是在主库上记录二进制日志,每次准备提交事务完成数据更新前,主库将数据更新的事件记录到一进制日志中。MySQL 会按事务提交的顺序而非每条语句的执行顺序来记录二进制日志。在记录二进制日志后,主库会告诉存储引擎可以提交事务了。
下一步,备库将主库的二进制日志复制到其本地的中继日志中。备库首先会启动一个工作的 IO线程。IO线程跟主库建立一个普通的客户端连接,然后在主库上启动一个特殊的二进制转储线程,这个线程会读取主库上二进制日志中的事件。它不会对事件进行轮询。如果该线程追赶上了主库将进入睡眠状态。直到主库发送信号量通知其有新的事件产生时才会被唤醒,备库IO线程会将接收到的事件记录到中继日志中。
备库的 SQL 线程执行最后一步,该线程从中继日志中读取事件并在备库执行,从而实现备库数据的事新。当 SQL 线程追赶上IO线程时,中继日志通常已经在系统缓存中,所以中继日志的开销很低。SQL线程执行的时间也可以通过配置选项来决定是否写人其自己的二进制日志中。