MySQL 涉及的内容非常非常非常多,所以面试题也容易写的杂乱。当年,我们记着几个一定要掌握的重心:
重点的题目添加了【重点】前缀。
因为 MySQL 还会有部分内容和运维相关度比较高,所以本文我们分成两部分【开发】【运维】两部分。
免费、流行、够用。
当然,这个回答要稍微润色下。不过一般,很少问这个问题了。
艿艿:重点在于反模式的回答。实际开发中,不会严格遵守三范式。
胖友直接看 《服务端指南 数据存储篇 | MySQL(07) 范式与反模式》 。
MySQL 支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。具体可以看看 《MySQL 数据类型》 文档。
MySQL 中 varchar 与 char 的区别?varchar(50) 中的 50 代表的涵义?
int(11) 中的 11 代表什么涵义?
int(11) 中的 11 ,不影响字段存储的范围,只影响展示效果。具体可以看看 《MySQL 中 int 长度的意义》 文章。
金额(金钱)相关的数据,选择什么数据类型?
一张表,里面有 ID 自增主键,当 insert 了 17 条记录之后,删除了第 15,16,17 条记录,再把 MySQL 重启,再 insert 一条记录,这条记录的 ID 是 18 还是 15?
最后,还可以跟面试官装个 x ,生产数据,不建议进行物理删除记录。
表中有大字段 X(例如:text 类型),且字段 X 不会经常更新,以读为为主,请问您是选择拆成子表,还是继续放一起?写出您这样选择的理由
拆带来的问题:连接消耗 + 存储拆分空间。
如果能容忍拆分带来的空间问题,拆的话最好和经常要查询的表的主键在物理结构上放置在一起(分区) 顺序 IO ,减少连接消耗,最后这是一个文本列再加上一个全文索引来尽量抵消连接消耗。
不拆可能带来的问题:查询性能。
如果能容忍不拆分带来的查询性能损失的话,上面的方案在某个极致条件下肯定会出现问题,那么不拆就是最好的选择。
实际场景下,例如说商品表数据量比较大的情况下,会将商品描述单独存储到一个表中。即,使用拆的方案。
MySQL 提供了多种的存储引擎:
具体每种存储引擎的介绍,可以看看 《数据库存储引擎》 。
如何选择合适的存储引擎?
提供几个选择标准,然后按照标准,选择对应的存储引擎即可,也可以根据 常用引擎对比 来选择你使用的存储引擎。使用哪种引擎需要根据需求灵活选择,一个数据库中多个表可以使用不同的引擎以满足各种性能和实际需求。使用合适的存储引擎,将会提高整个数据库的性能。
是否需要支持事务。
对索引和缓存的支持。
是否需要使用热备。
崩溃恢复,能否接受崩溃。
存储的限制。
是否需要外键支持。
艿艿:目前开发已经不考虑外键,主要原因是性能。具体可以看看 《从 MySQL 物理外键开始的思考》 文章。
目前,MySQL 默认的存储引擎是 InnoDB ,并且也是最主流的选择。主要原因如下:
在 MySQL5.1 以及之前的版本,默认的存储引擎是 MyISAM ,但是目前已经不再更新,且它有几个比较关键的缺点:
艿艿:也就是说,我们不需要花太多力气在 MyISAM 的学习上。
请说明 InnoDB 和 MyISAM 的区别
InnoDB | MyISAM | |
---|---|---|
事务 | 支持 | 不支持 |
存储限制 | 64TB | 无 |
锁粒度 | 行锁 | 表锁 |
崩溃后的恢复 | 支持 | 不支持 |
外键 | 支持 | 不支持 |
全文检索 | 5.7 版本后支持 | 支持 |
更完整的对比,可以看看 《数据库存储引擎》 的 「常用引擎对比」 小节。
请说说 InnoDB 的 4 大特性?
艿艿:貌似我面试没被问过…反正,我是没弄懂过~~
为什么 SELECT COUNT(*) FROM table 在 InnoDB 比 MyISAM 慢?
对于 SELECT COUNT(*) FROM table
语句,在没有 WHERE
条件的情况下,InnoDB 比 MyISAM 可能会慢很多,尤其在大表的情况下。因为,InnoDB 是去实时统计结果,会全表扫描;而 MyISAM 内部维持了一个计数器,预存了结果,所以直接返回即可。
详细的原因,胖友可以看看 《高性能 MySQL 之 Count 统计查询》 博客。
各种不同 MySQL 版本的 Innodb 的改进?
艿艿:这是一个选择了解的问题。
MySQL5.6 下 Innodb 引擎的主要改进:
MySQL5.7 下 Innodb 引擎的主要改进:
1、修改 varchar 字段长度有时可以使用
这里的“有时”,指的是也有些限制。可见 《MySQL 5.7 online ddl 的一些改进》 。
2、Buffer pool 支持在线改变大小
3、Buffer pool 支持导出部分比例
4、支持新建 innodb tablespace,并可以在其中创建多张表
5、磁盘临时表采用 innodb 存储,并且存储在 innodb temp tablespace 里面,以前是 MyISAM 存储
6、透明表空间压缩功能
索引,类似于书籍的目录,想找到一本书的某个特定的主题,需要先找到书的目录,定位对应的页码。
MySQL 中存储引擎使用类似的方式进行查询,先去索引中查找对应的值,然后根据匹配的索引找到对应的数据行。
索引有什么好处?
索引有什么坏处?
索引的使用场景?
1、对非常小的表,大部分情况下全表扫描效率更高。
2、对中大型表,索引非常有效。
3、特大型的表,建立和使用索引的代价随着增长,可以使用分区技术来解决。
实际场景下,MySQL 分区表很少使用,原因可以看看 《互联网公司为啥不使用 MySQL 分区表?》 文章。
对于特大型的表,更常用的是“分库分表”,目前解决方案有 Sharding Sphere、MyCAT 等等。
索引的类型?
索引,都是实现在存储引擎层的。主要有六种类型:
1、普通索引:最基本的索引,没有任何约束。
2、唯一索引:与普通索引类似,但具有唯一性约束。
3、主键索引:特殊的唯一索引,不允许有空值。
4、复合索引:将多个列组合在一起创建索引,可以覆盖多个列。
5、外键索引:只有InnoDB类型的表才可以使用外键索引,保证数据的一致性、完整性和实现级联操作。
6、全文索引:MySQL 自带的全文索引只能用于 InnoDB、MyISAM ,并且只能对英文进行全文检索,一般使用全文索引引擎。
常用的全文索引引擎的解决方案有 Elasticsearch、Solr 等等。最为常用的是 Elasticsearch 。
具体的使用,可以看看 《服务端指南 数据存储篇 | MySQL(03) 如何设计索引》 。
MySQL 索引的“创建”原则?
注意,是“创建”噢。
1、最适合索引的列是出现在 WHERE
子句中的列,或连接子句中的列,而不是出现在 SELECT
关键字后的列。
2、索引列的基数越大,索引效果越好。
具体为什么,可以看看如下两篇文章:
- 《MySQL 索引基数》 理解相对简单
- 《低基数索引为什么会对性能产生负面影响》 写的更原理,所以较为难懂。
3、根据情况创建复合索引,复合索引可以提高查询效率。
因为复合索引的基数会更大。
4、避免创建过多的索引,索引会额外占用磁盘空间,降低写操作效率。
5、主键尽可能选择较短的数据类型,可以有效减少索引的磁盘占用提高查询效率。
6、对字符串进行索引,应该定制一个前缀长度,可以节省大量的索引空间。
MySQL 索引的“使用”注意事项?
注意,是“使用”噢。
1、应尽量避免在 WHERE
子句中使用 !=
或 <>
操作符,否则将引擎放弃使用索引而进行全表扫描。优化器将无法通过索引来确定将要命中的行数,因此需要搜索该表的所有行。
注意,
column IS NULL
也是不可以使用索引的。
2、应尽量避免在 WHERE
子句中使用 OR
来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:SELECT id FROM t WHERE num = 10 OR num = 20
。
3、应尽量避免在 WHERE
子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。
4、应尽量避免在 WHERE
子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。
5、不要在 WHERE
子句中的 =
左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
6、复合索引遵循前缀原则。
7、如果 MySQL 评估使用索引比全表扫描更慢,会放弃使用索引。如果此时想要索引,可以在语句中添加强制索引。
8、列类型是字符串类型,查询时一定要给值加引号,否则索引失效。
9、LIKE
查询,%
不能在前,因为无法使用索引。如果需要模糊匹配,可以使用全文索引。
关于这块,可以看看 《服务端指南 数据存储篇 | MySQL(04) 索引使用的注意事项》 文章,写的更加细致。
以下三条 SQL 如何建索引,只建一条怎么建?
WHERE a = 1 AND b = 1
WHERE b = 1
WHERE b = 1 ORDER BY time DESC
CREATE INDEX table1_b_a_time ON index_test01(b, a, time)
。WHERE
子句后面的列顺序,以匹配复合索引顺序。想知道一个查询用到了哪个索引,如何查看?
EXPLAIN
显示了 MYSQL 如何使用索引来处理 SELECT 语句以及连接表,可以帮助选择更好的索引和写出更优化的查询语句。
使用方法,在 SELECT
语句前加上 EXPLAIN
就可以了。 《MySQL explain 执行计划详细解释》 。
解释 MySQL 索引的原理,篇幅会比较长,并且网络上已经有靠谱的资料可以看,所以艿艿这里整理了几篇,胖友可以对照着看。
《MySQL索引背后的数据结构及算法原理》
写的一点都不好,艿艿在地铁反复看了 10 遍。
强烈推荐!!!!
《MySQL 索引原理》
《深入理解 MySQL 索引原理和实现 —— 为什么索引可以加速查询?》
下面,艿艿对关键知识做下整理,方便胖友回顾。
几篇好一点的文章:
《MySQL索引背后的数据结构及算法原理》
《MySQL 索引原理》
《深入理解 MySQL 索引原理和实现 —— 为什么索引可以加速查询?》
MySQL 有哪些索引方法?
在 MySQL 中,我们可以看到两种索引方式:
B-Tree 索引。
Hash 索引。
《MySQL BTree 索引和 hash 索引的区别》
什么是 B-Tree 索引?
B-Tree 是为磁盘等外存储设备设计的一种平衡查找树。因此在讲 B-Tree 之前先了解下磁盘的相关知识。
mysql> show variables like 'innodb_page_size';
B-Tree 结构的数据可以让系统高效的找到数据所在的磁盘块。为了描述B-Tree,首先定义一条记录为一个二元组 [key, data] ,key 为记录的键值,对应表中的主键值,data 为一行记录中除主键外的数据。对于不同的记录,key值互不相同。
一棵 m 阶的 B-Tree 有如下特性:
B-Tree 中的每个节点根据实际情况可以包含大量的关键字信息和分支,如下图所示为一个 3 阶的 B-Tree:
模拟查找 key 为 29 的过程:
分析上面过程,发现需要 3 次磁盘 I/O 操作,和 3 次内存查找操作。由于内存中的 key 是一个有序表结构,可以利用二分法查找提高效率。而 3 次磁盘 I/O 操作是影响整个 B-Tree 查找效率的决定因素。B-Tree 相对于 AVLTree 缩减了节点个数,使每次磁盘 I/O 取到内存的数据都发挥了作用,从而提高了查询效率。
什么是 B+Tree 索引?
B+Tree 是在 B-Tree 基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用 B+Tree 实现其索引结构。
从上一节中的 B-Tree 结构图中可以看到,每个节点中不仅包含数据的 key 值,还有 data 值。而每一个页的存储空间是有限的,如果 data 数据较大时将会导致每个节点(即一个页)能存储的 key 的数量很小,当存储的数据量很大时同样会导致 B-Tree 的深度较大,增大查询时的磁盘 I/O 次数,进而影响查询效率。在 B+Tree 中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储 key 值信息,这样可以大大加大每个节点存储的 key 值数量,降低 B+Tree 的高度。
B+Tree 相对于 B-Tree 有几点不同:
将上一节中的 B-Tree 优化,由于 B+Tree 的非叶子节点只存储键值信息,假设每个磁盘块能存储 4 个键值及指针信息,则变成 B+Tree 后其结构如下图所示:
可能上面例子中只有 22 条数据记录,看不出 B+Tree 的优点,下面做一个推算:
B+Tree 有哪些索引类型?
在 B+Tree 中,根据叶子节点的内容,索引类型分为主键索引和非主键索引。
辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的聚集索引键,即主键。当通过辅助索引来查询数据时,需要进过两步:
另外,InnoDB 通过主键聚簇数据,如果没有定义主键,会选择一个唯一的非空索引代替,如果没有这样的索引,会隐式定义个主键作为聚簇索引。
再另外,可能有胖友有和艿艿的一样疑惑,在辅助索引如果相同的索引怎么存储?最终存储到 B+Tree 非子节点中时,它们对应的主键 ID 是不同的,所以妥妥的。如下图所示:
聚簇索引的注意点有哪些?
聚簇索引表最大限度地提高了 I/O 密集型应用的性能,但它也有以下几个限制:
1、插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于 InnoDB 表,我们一般都会定义一个自增的 ID 列为主键。
关于这一点,可能面试官会换一个问法。例如,为什么主键需要是自增 ID ,又或者为什么主键需要带有时间性关联。
2、更新主键的代价很高,因为将会导致被更新的行移动。因此,对于InnoDB 表,我们一般定义主键为不可更新。
MySQL 默认情况下,主键是允许更新的。对于 MongoDB ,其 主键是不允许更新的。
3、二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。
当然,有一种情况可以无需二次查找,基于非主键索引查询,但是查询字段只有主键 ID ,那么在二级索引中就可以查找到。
4、主键 ID 建议使用整型。因为,每个主键索引的 B+Tree 节点的键值可以存储更多主键 ID ,每个非主键索引的 B+Tree 节点的数据可以存储更多主键 ID 。
什么是索引的最左匹配特性?
当 B+Tree 的数据项是复合的数据结构,比如索引 (name, age, sex)
的时候,B+Tree 是按照从左到右的顺序来建立搜索树的。
(张三, 20, F)
这样的数据来检索的时候,B+Tree 会优先比较 name 来确定下一步的所搜方向,如果 name 相同再依次比较 age 和 sex ,最后得到检索的数据。(20, F)
这样的没有 name 的数据来的时候,B+Tree 就不知道下一步该查哪个节点,因为建立搜索树的时候 name 就是第一个比较因子,必须要先根据 name 来搜索才能知道下一步去哪里查询。(张三, F)
这样的数据来检索时,B+Tree 可以用 name 来指定搜索方向,但下一个字段 age 的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是 F 的数据了。这个是非常重要的性质,即索引的最左匹配特性。
MyISAM 索引实现?
MyISAM 索引的实现,和 InnoDB 索引的实现是一样使用 B+Tree ,差别在于 MyISAM 索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。
MyISAM 索引与 InnoDB 索引的区别?
1、插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于 InnoDB 表,我们一般都会定义一个自增的 ID 列为主键。
关于这一点,可能面试官会换一个问法。例如,为什么主键需要是自增 ID ,又或者为什么主键需要带有时间性关联。
2、更新主键的代价很高,因为将会导致被更新的行移动。因此,对于InnoDB 表,我们一般定义主键为不可更新。
MySQL 默认情况下,主键是允许更新的。对于 MongoDB ,其 主键是不允许更新的。
3、二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。
当然,有一种情况可以无需二次查找,基于非主键索引查询,但是查询字段只有主键 ID ,那么在二级索引中就可以查找到。
4、主键 ID 建议使用整型。因为,每个主键索引的 B+Tree 节点的键值可以存储更多主键 ID ,每个非主键索引的 B+Tree 节点的数据可以存储更多主键 ID 。
1、插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于 InnoDB 表,我们一般都会定义一个自增的 ID 列为主键。
关于这一点,可能面试官会换一个问法。例如,为什么主键需要是自增 ID ,又或者为什么主键需要带有时间性关联。
2、更新主键的代价很高,因为将会导致被更新的行移动。因此,对于InnoDB 表,我们一般定义主键为不可更新。
MySQL 默认情况下,主键是允许更新的。对于 MongoDB ,其 主键是不允许更新的。
3、二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。
当然,有一种情况可以无需二次查找,基于非主键索引查询,但是查询字段只有主键 ID ,那么在二级索引中就可以查找到。
4、主键 ID 建议使用整型。因为,每个主键索引的 B+Tree 节点的键值可以存储更多主键 ID ,每个非主键索引的 B+Tree 节点的数据可以存储更多主键 ID 。
1、插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于 InnoDB 表,我们一般都会定义一个自增的 ID 列为主键。
关于这一点,可能面试官会换一个问法。例如,为什么主键需要是自增 ID ,又或者为什么主键需要带有时间性关联。
2、更新主键的代价很高,因为将会导致被更新的行移动。因此,对于InnoDB 表,我们一般定义主键为不可更新。
MySQL 默认情况下,主键是允许更新的。对于 MongoDB ,其 主键是不允许更新的。
3、二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。
当然,有一种情况可以无需二次查找,基于非主键索引查询,但是查询字段只有主键 ID ,那么在二级索引中就可以查找到。
4、主键 ID 建议使用整型。因为,每个主键索引的 B+Tree 节点的键值可以存储更多主键 ID ,每个非主键索引的 B+Tree 节点的数据可以存储更多主键 ID 。
事务就是对一系列的数据库操作(比如插入多条数据)进行统一的提交或回滚操作,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。
这样可以防止出现脏数据,防止数据库数据出现问题。
事务的特性指的是?
指的是 ACID ,如下图所示:
事务的并发问题?
实际场景下,事务并不是串行的,所以会带来如下三个问题:
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
MySQL 事务隔离级别会产生的并发问题?
READ UNCOMMITTED(未提交读):事务中的修改,即使没有提交,对其他事务也都是可见的。
会导致脏读。
READ COMMITTED(提交读):事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。
会导致不可重复读。
这个隔离级别,也可以叫做“不可重复读”。
REPEATABLE READ(可重复读):一个事务按相同的查询条件读取以前检索过的数据,其他事务插入了满足其查询条件的新数据。产生幻行。
会导致幻读。
SERIALIZABLE(可串行化):强制事务串行执行。
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
读已提交(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是(x) |
串行化(serializable) | 否 | 否 | 否 |
处,MySQL 因为其间隙锁的特性,导致其在可重复读(repeatable-read)的隔离级别下,不存在幻读问题。也就是说,上图
处,需要改成“否”!!!!表锁是日常开发中的常见问题,因此也是面试当中最常见的考察点,当多个查询同一时刻进行数据修改时,就会产生并发控制的问题。MySQL 的共享锁和排他锁,就是读锁和写锁。
锁的粒度?
什么是悲观锁?什么是乐观锁?
1)悲观锁
它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
在悲观锁的情况下,为了保证事务的隔离性,就需要一致性锁定读。读取数据时给加锁,其它事务无法修改这些数据。修改删除数据时也要加锁,其它事务无法读取这些数据。
2)乐观锁
相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。
而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
什么是死锁?
多数情况下,可以认为如果一个资源被锁定,它总会在以后某个时间被释放。而死锁发生在当多个进程访问同一数据库时,其中每个进程拥有的锁都是其他进程所需的,由此造成每个进程都无法继续下去。简单的说,进程 A 等待进程 B 释放他的资源,B 又等待 A 释放他的资源,这样就互相等待就形成死锁。
虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件:
下列方法有助于最大限度地降低死锁:
设置获得锁的超时时间。
通过超时,至少保证最差最差最差情况下,可以有退出的口子。
按同一顺序访问对象。
这个是最重要的方式。
避免事务中的用户交互。
保持事务简短并在一个批处理中。
使用低隔离级别。
使用绑定连接。
MySQL 中 InnoDB 引擎的行锁是通过加在什么上完成(或称实现)的?为什么是这样子的??
InnoDB 是基于索引来完成行锁。例如:SELECT * FROM tab_with_index WHERE id = 1 FOR UPDATE
。
FOR UPDATE
可以根据条件来完成行锁锁定,并且 id 是有索引键的列,如果 id 不是索引键那么 InnoDB 将完成表锁,并发将无从谈起。MySQL 查询执行的顺序是:
(1) SELECT
(2) DISTINCT
(3) FROM
(4) JOIN
(5) ON
(6) WHERE
(7) GROUP BY
(8) HAVING
(9) ORDER BY
(10) LIMIT
具体的,可以看看 《SQL 查询之执行顺序解析》 文章。
可以看看如下几篇文章:
另外,除了从 SQL 层面进行优化,也可以从服务器硬件层面,进一步优化 MySQL 。具体可以看看 《MySQL 数据库性能优化之硬件优化》 。
当 CPU 飙升到 500% 时,先用操作系统命令 top 命令观察是不是 mysqld 占用导致的,如果不是,找出占用高的进程,并进行相关处理。
如果此时是 IO 压力比较大,可以使用 iostat 命令,定位是哪个进程占用了磁盘 IO 。
如果是 mysqld 造成的,使用 show processlist
命令,看看里面跑的 Session 情况,是不是有消耗资源的 SQL 在运行。找出消耗高的 SQL ,看看执行计划是否准确, index 是否缺失,或者实在是数据量太大造成。一般来说,肯定要 kill 掉这些线程(同时观察 CPU 使用率是否下降),等进行相应的调整(比如说加索引、改 SQL 、改内存参数)之后,再重新跑这些 SQL。
也可以查看 MySQL 慢查询日志,看是否有慢 SQL 。
也有可能是每个 SQL 消耗资源并不多,但是突然之间,有大量的 Session 连进来导致 CPU 飙升,这种情况就需要跟应用一起来分析为何连接数会激增,再做出相应的调整,比如说限制连接数等。
在 MySQL 服务器运行缓慢的情况下输入什么命令能缓解服务器压力?
1)检查系统的状态
通过操作系统的一些工具检查系统的状态,比如 CPU、内存、交换、磁盘的利用率,根据经验或与系统正常时的状态相比对,有时系统表面上看起来看空闲,这也可能不是一个正常的状态,因为 CPU 可能正等待IO的完成。除此之外,还应观注那些占用系统资源(CPU、内存)的进程。
2)检查 MySQL 参数
3)检查 MySQL 相关状态值
有多少种日志?
日志的存放形式?
事务是如何通过日志来实现的,说得越深入越好
艿艿:这个流程的理解还是比较简单的,实际思考实现感觉还是蛮复杂的。
基本流程如下:
各种日志格式的涵义
binlog 有三种格式类型,分别如下:
1)Statement
每一条会修改数据的 SQL 都会记录在 binlog 中。
优点:不需要记录每一行的变化,减少了 binlog 日志量,节约了 IO,提高性能。(相比 row 能节约多少性能与日志量,这个取决于应用的 SQL 情况,正常同一条记录修改或者插入 row 格式所产生的日志量还小于 Statement 产生的日志量,但是考虑到如果带条件的 update 操作,以及整表删除,alter 表等操作,ROW 格式会产生大量日志,因此在考虑是否使用 ROW 格式日志时应该跟据应用的实际情况,其所产生的日志量会增加多少,以及带来的 IO 性能问题。)
缺点:由于记录的只是执行语句,为了这些语句能在 slave 上正确运行,因此还必须记录每条语句在执行的时候的一些相关信息,以保证所有语句能在 slave 得到和在 master 端执行时候相同 的结果。另外 MySQL 的复制,像一些特定函数功能,slave 可与 master 上要保持一致会有很多相关问题(如 sleep()
函数,last_insert_id()
,以及 user-defined functions(udf) 会出现问题)。
使用以下函数的语句也无法被复制:
LOAD_FILE()
UUID()
USER()
FOUND_ROWS()
SYSDATE()
(除非启动时启用了 --sysdate-is-now
选项)
同时在 INSERT …SELECT 会产生比 RBR 更多的行级锁 。
2)Row
不记录 SQL 语句上下文相关信息,仅保存哪条记录被修改。
3)Mixedlevel
是以上两种 level 的混合使用。
MySQL 会根据执行的每一条具体的 SQL 语句来区分对待记录的日志形式,也就是在 Statement 和 Row 之间选择 一种。
新版本的 MySQL 中对 row level 模式也被做了优化,并不是所有的修改都会以 row level 来记录。
适用场景?
在一条 SQL 操作了多行数据时, Statement 更节省空间,Row 更占用空间。但是, Row 模式更可靠。
因为,互联网公司,使用 MySQL 的功能相对少,基本不使用存储过程、触发器、函数的功能,选择默认的语句模式,Statement Level(默认)即可。
结合第一个问题,每一种日志格式在复制中的优劣?
所以,这是在占用空间和可靠之间的选择。
如何在线正确清理 MySQL binlog?
MySQL 中的 binlog 日志记录了数据中的数据变动,便于对数据的基于时间点和基于位置的恢复。但日志文件的大小会越来越大,占用大量的磁盘空间,因此需要定时清理一部分日志信息。
# 首先查看主从库正在使用的binlog文件名称
show master(slave) status
# 删除之前一定要备份
purge master logs before'2017-09-01 00:00:00'; # 删除指定时间前的日志
purge master logs to'mysql-bin.000001'; # 删除指定的日志文件
# 自动删除:通过设置binlog的过期时间让系统自动删除日志
show variables like 'expire_logs_days'; # 查看过期时间
set global expire_logs_days = 30; # 设置过期时间
MySQL 的主从复制是基于如下 3 个线程的交互(多线程复制里面应该是 4 类线程):
MySQL 如何保证复制过程中数据一致性?
MySQL 如何解决主从复制的延时性?
5.5 是单线程复制,5.6 是多库复制(对于单库或者单表的并发操作是没用的),5.7 是真正意义的多线程复制,它的原理是基于 group commit, 只要 master 上面的事务是 group commit 的,那 slave 上面也可以通过多个 worker线程去并发执行。 和 MairaDB10.0.0.5 引入多线程复制的原理基本一样。
工作遇到的复制 bug 的解决方法?
5.6 的多库复制有时候自己会停止,我们写了一个脚本重新 start slave 。
你是否做过主从一致性校验,如果有,怎么做的,如果没有,你打算怎么做?
主从一致性校验有多种工具 例如 checksum、mysqldiff、pt-table-checksum 等。
具体的,胖友可以看看 《MySQL 高级备份策略》 。主要有几个知识点:
数据的备份类型
【常用】完全备份
这是大多数人常用的方式,它可以备份整个数据库,包含用户表、系统表、索引、视图和存储过程等所有数据库对象。但它需要花费更多的时间和空间,所以,一般推荐一周做一次完全备份。
增量备份
它是只备份数据库一部分的另一种方法,它不使用事务日志,相反,它使用整个数据库的一种新映象。它比最初的完全备份小,因为它只包含自上次完全备份以来所改变的数据库。它的优点是存储和恢复速度快。推荐每天做一次差异备份。
【常用】事务日志备份
事务日志是一个单独的文件,它记录数据库的改变,备份的时候只需要复制自上次备份以来对数据库所做的改变,所以只需要很少的时间。为了使数据库具有鲁棒性,推荐每小时甚至更频繁的备份事务日志。
文件备份
数据库可以由硬盘上的许多文件构成。如果这个数据库非常大,并且一个晚上也不能将它备份完,那么可以使用文件备份每晚备份数据库的一部分。由于一般情况下数据库不会大到必须使用多个文件存储,所以这种备份不是很常用。
备份数据的类型
备份工具
MySQL 几种备份方式?
MySQL 一般有 3 种备份方式。
1)逻辑备份
使用 MySQL 自带的 mysqldump 工具进行备份。备份成sql文件形式。
2)物理备份
艿艿:因为现在主流是 InnoDB ,所以基本不再考虑这种方式。
直接拷贝只适用于 MyISAM 类型的表。这种类型的表是与机器独立的。但实际情况是,你设计数据库的时候不可能全部使用 MyISAM 类型表。你也不可能因为 MyISAM 类型表与机器独立,方便移植,于是就选择这种表,这并不是选择它的理由。
3)双机热备份。
当数据量太大的时候备份是一个很大的问题,MySQL 数据库提供了一种主从备份的机制,也就是双机热备。
数据库不能停机,请问如何备份? 如何进行全备份和增量备份?
可以使用逻辑备份和双机热备份。
my.ini
文件中配置备份路径即可,重启 MySQL 服务器,增量备份就启动了。你的备份工具的选择?备份计划是怎么样的?
视库的大小来定,一般来说 100G 内的库,可以考虑使用 mysqldump 来做,因为 mysqldump 更加轻巧灵活,备份时间选在业务低峰期,可以每天进行都进行全量备份(mysqldump 备份出来的文件比较小,压缩之后更小)。
100G 以上的库,可以考虑用 xtrabackup 来做,备份速度明显要比 mysqldump 要快。一般是选择一周一个全备,其余每天进行增量备份,备份时间为业务低峰期。
备份恢复时间是多长?
物理备份恢复快,逻辑备份恢复慢。
这里跟机器,尤其是硬盘的速率有关系,以下列举几个仅供参考:
逻辑导入时间一般是备份时间的 5 倍以上。
备份恢复失败如何处理?
首先在恢复之前就应该做足准备工作,避免恢复的时候出错。比如说备份之后的有效性检查、权限检查、空间检查等。如果万一报错,再根据报错的提示来进行相应的调整。
mysqldump 和 xtrabackup 实现原理?
1)mysqldump
mysqldump 是最简单的逻辑备份方式。
–master-data=1 –single-transaction
选项,在事务开始时刻,记录下 binlog pos 点,然后利用 MVCC 来获取一致的数据,由于是一个长事务,在写入和更新量很大的数据库上,将产生非常多的 undo ,显著影响性能,所以要慎用。2)xtrabackup
xtrabackup 实际上是物理备份+逻辑备份的组合。
flush tables with lock
来获得全局锁,开始拷贝这些不再变化的文件,同时获得 binlog 位置,拷贝结束后释放锁,也停止对 redo log 的监视。如何从 mysqldump 产生的全库备份中只恢复某一个库、某一张表?
具体可见 《MySQL 全库备份中恢复某个库和某张表以及 mysqldump 参数 –ignore-table 介绍》 文章。
艿艿:这块艿艿懂的少,主要找了一些网络上的资料。
对于简历中写有熟悉 MySQL 高可用方案?
我一般先问他现在管理的数据库架构是什么,如果他只说出了主从,而没有说任何 HA 的方案,那么我就可以判断出他没有实际的 HA 经验。
不过这时候也不能就是断定他不懂 MySQL 高可用,也许是没有实际机会去使用,那么我就要问 MMM 以及 MHA 以及 MM + keepalived 等的原理、实现方式以及它们之间的优势和不足了,一般这种情况下,能说出这个的基本没有。
感兴趣的胖友,可以看看:
错误日志:记录了当 mysqld 启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。
二进制文件:记录了所有的 DDL(数据定义语言)语句和 DML(数据操纵语言)语句,不包括数据查询语句。语句以“事件”的形式保存,它描述了数据的更改过程。(定期删除日志,默认关闭)。
就是我们上面看到的 MySQL binlog 日志。
查询日志:记录了客户端的所有语句,格式为纯文本格式,可以直接进行读取。(log 日志中记录了所有数据库的操作,对于访问频繁的系统,此日志对系统性能的影响较大,建议关闭,默认关闭)。
慢查询日志:慢查询日志记录了包含所有执行时间超过参数long_query_time(单位:秒)所设置值的 SQL 语句的日志。(纯文本格式)
重要,一定要开启。
另外,错误日志和慢查询日志的详细解释,可以看看 《MySQL 日志文件之错误日志和慢查询日志详解》 文章。
你是如何监控你们的数据库的?
监控的工具有很多,例如 Zabbix ,Lepus ,我这里用的是 Lepus 。
使用 pt-online-schema-change ,具体可以看看 《MySQL 大表在线 DML 神器–pt-online-schema-change》 文章。
另外,还有一些其它的工具,胖友可以搜索下。