面试题三:MySQL
-
MySQL有哪些存储引擎?
MyISAM、InnoDB、CSV、Memory等
MyISAM和InnoDB比较:
InnoDB MyISAM 事务 支持 不支持 存储限制 64TB 无 锁粒度 行锁 表锁 崩溃后的恢复 支持 不支持 外键 支持 不支持 全文检索 5.7后支持 支持 InnoDB是默认的存储引擎,也是主流的选择,原因:
- 支持事务;
- 支持行锁和表锁,能支持更大的并发量;
- 查询不加锁,完全不影响查询;
- 支持崩溃后恢复。
-
为什么
select count(*) from table
在MyISAM上比InnoDB上快?在不带有where条件的情况下,确实是MyISAM上比InnoDB上快,原因是InnoDB需要实时查询,但是MyISAM是存储引擎内部带有的计数器,因此比较快;
在带有where条件的情况下,这个结论就不成立了。
-
count(*)
和count(column)
有什么区别?count()
函数既可以统计某个列值的数量,也可以统计行数。在统计列值的时候,要求列值非NULL,也就是
count(column)
统计的是某一列的非空的值的结果数;count(*)
会直接忽略所有的列,直接统计所有的行数,统计结果集的行数时,直接写count(*)
意义明确且性能也较好。 -
什么是索引?
MySQL中存储引擎使用类似书籍目录的方式查询数据,先去索引中查找对应的值,然后根据匹配的索引找到对应的数据行。
-
索引有什么好处?
- 提高数据检索速度,降低数据库IO成本,使用索引的意义就是通过缩小数据库中需要查询的记录的数目从而加快检索速度;
- 降低数据排序的成本,索引之所以查询速度较快是因为已经做了排序,如果恰好该字段也需要排序,那么就可以降低数据排序的成本,降低CPU消耗;
-
索引有什么坏处?
- 占用存储空间:索引实际上也是一张表,记录了主键id和索引字段,一般以索引文件的形式存储在磁盘上;
- 降低数据更新的速度,数据更新,索引也需要跟着更新,从而降低数据更新速度;否则索引对应的物理数据可能不对,这也是索引失效的原因之一。
-
索引的使用场景?
-
小型的数据库表,全表扫描的效率更高;
-
中大型的数据库表,使用索引比较有效;
-
特大型的数据库表,创建和使用索引的代价随之增长,可以考虑使用分库分表来解决
一般不建议使用分区解决,因为一方面不可控,另外并发情况下分区表关联效率低下且容易出现全表锁。
-
-
索引的类型?
- 普通索引(单列索引):针对单列创建的索引,没有任何限制
- 唯一索引:与普通索引类似,但是要求值是唯一的,可以为null
- 主键索引:特殊的唯一索引,但是值不能为null
- 复合索引:多个字段联合形成的索引,适用最左匹配原则,因此复合索引中字段的顺序十分重要;
- 全文索引:为了优化类似
like %value%
类型的查询,避免全表扫描,可以使用全文索引,但是全文索引不支持中文,最好的方式还是使用ES。MySQL5.6之后的版本,InnoDB和MyISAM都支持全文索引,之前的版本只有MyISAM支持。 - 外键索引:只有InnoDB才支持该索引,目的是为了保证数据的一致性、完整性和实现级联操作。
-
MySQL索引的创建原则?
-
最适合创建索引的字段是
where
后面的条件字段或者连接语句字段,而不是select
之后的字段; -
索引列的基数越大,索引效果越好;
基数是指不重复的数据,如1,3,4,5,5,6,6中,基数是5
-
根据情况创建复合索引,复合索引可以提高查询效率;
-
索引的数量并不是越多越好,会额外占用内存空间,并且降低写数据的效率;
-
主键尽可能选择较短的数据类型,可以有效的减少索引的磁盘占用提高查询效率;
-
对字符串进行索引,应该定制一个前缀长度,可以节省大量的索引空间;
-
-
MySQL索引的使用注意事项?
-
不要在列上使用函数和进行运算,会导致全表扫面;
select * from table_name where year(pub_time) < 2020;
-
避免使用!=或者Not In或者<>等否定操作符;
-
避免使用OR来连接条件;
-
多个单列索引并不是最佳选择
MySQL只能使用一个索引,会从多个索引中选择最优的一个,因此创建多个单列索引未必是最优的
select * from news where news_year = 2017 and news_month = 1
如上sql语句,此时更适合创建复合索引,保证两个列都被覆盖。
-
复合索引的最左前缀原则
复合索引遵循最左前缀原则,只有查询条件中存在复合索引的第一个字段,复合索引才能生效,否则不生效
-
范围查询对多列查询的影响
范围查询会导致复合索引中范围字段列之后的索引列都不生效,如存在复合索引
news_publish_idx(publish_time, enable)
,有sql语句如下:select * from news where publish_time >= '2017-01-02' and publish_time <= '2017-01-08' and enable = 1
此时复合索引中的
enable
列的索引失效 -
索引不会包含含有NULL值的列
只要列中包含NULL值,就不会被包含在索引中,复合索引中只要有一列包含有NULL值,这一列就不会被包含在复合索引中。
-
隐式转换的影响
当查询条件左右两侧数据类型不一致的时候会发生隐式转换,隐式转换可能会导致索引失效
select * from news where date_str = 201701
因此在查询时要使用相同的数据类型查询。
-
like索引失效、
like value%
查询时索引生效,但是like %value%
形式的查询索引就不生效了。
-
-
MySQL索引的原理?
12-21题
-
MySQL有哪些索引的方法?
B-Tree索引:常用de索引类型
Hash索引
- 哈希索引只能用于
=
、!=
、IN
查询,不能用于范围查询; - 哈希索引被用来避免数据的排序操作;
- 哈希索引不能用来部分索引键查询;
- 哈希索引在任何时候都不能避免权标扫描;
- 哈希冲突时使用哈希索引性能未必有B-Tree高。
- 哈希索引只能用于
-
磁盘相关知识?
- 系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是需要什么取什么。
- InnoDB存储引擎中有页(Page)的概念,页是其磁盘管理的最小单位。InnoDB 存储引擎中默认每个页的大小为 16 KB,可通过参数 innodb_page_size 将页的大小设置为 4K、8K、16K;
- 而系统一个磁盘块的存储空间往往没有这么大,因此 InnoDB 每次申请磁盘空间时都会是若干地址连续磁盘块来达到页的大小 16KB 。InnoDB 在把磁盘数据读入到磁盘时会以页为基本单位,在查询数据时如果一个页中的每条数据都能有助于定位数据记录的位置,这将会减少磁盘 I/O 次数,提高查询效率。
-
什么是B-Tree索引?
B-Tree 是为磁盘等外存储设备设计的一种平衡查找树。
-
什么是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非叶子节点只存储key信息;
- 所有叶子节点之间都有一个双向的链指针;
- 数据记录都存放在叶子节点中;
-
B-Tree 有哪些索引类型?
-
主键索引
主键索引的叶子节点存的数据是整行数据( 即具体数据 )。在 InnoDB 里,主键索引也被称为聚集索引(clustered index)。
-
非主键索引
非主键索引的叶子节点存的数据是整行数据的主键,键值是索引。在 InnoDB 里,非主键索引也被称为辅助索引(secondary index)。
-
-
聚簇索引的注意点有哪些?
InnoDB中的索引被称为聚簇索引,最大限度的提高了I/O密集型应用的性能,但是也存在着几点限制:
- 插入速度严重依赖插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此InnoDB表一般采用自增主键的方式。
- 更新主键的代价很高,因为会导致被更新的行移动,因此InnoDB一般设置主键不可更新;
- 二级索引(辅助索引)访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到数据;
- 主键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 的数据了。
sql语句在编写时,字段的顺序无所谓,MySQL会进行优化,只需要满足符合索引的字段即可,如下所示也是可以支持索引的:
select * from tableName where age = 20 and name = '张三' and sex = '男'
- 比如当
-
MyISAM索引的实现?
MyISAM索引使用B+Tree实现,但是需要注意的是MyISAM的数据文件和索引文件是拆分开的,索引文件仅保存数据记录的地址。
-
主键索引
叶子节点存放的是数据记录的地址,示意图如下:
-
辅助索引
在MyISAM索引中,主键索引和辅助索引的结构是完全相同的,唯一区别是主键索引要求key值是唯一的,但是辅助索引的key值可以重复。
-
-
MyISAM索引和InnoDB索引的区别?
相同点:
- 都是基于B+Tree实现的
不同点:
-
InnoDB是聚簇索引,MyISAM是非聚簇索引;
-
InnoDB的主键索引,叶子节点存储的直接就是行数据,查询效率很高;
-
MyISAM索引的叶子节点存储的是行数据地址,因此需要再寻址一次才能查询到数据;
-
InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效;
覆盖索引,指的是基于非主键索引查询,但是查询字段只有主键 ID ,那么在二级索引中就可以查找到
-
MySQL的四种事务隔离级别?
参考25题
-
事务的四种特性?
原子性(A):一个事务中的所有操作,要么全部成功,要么全部不成功,不会存在中间状态;事务在执行过程中出现错误,那么会恢复到发生事务之前的状态,就像这个事务从来没有发生过一样,也即事务不可分割,不可约简
一致性(C):事务开始之前和事务结束之后,数据库的完整性没有被破坏。
隔离性(I): 数据库允许多个并发事务同时对数据读读取和修改的能力,隔离性能够防止多个事务并发执行时由于交叉执行而导致的数据不一致问题。
事务的隔离级别分为四种:
- 读未提交
- 读提交
- 可重复读
- 串行化
持久性(D):事务结束以后,对数据修改就是永久性的不可修改,即使系统故障也不会丢失。
-
事务的并发问题?
-
脏读。
事务A读取了事务B的数据,但是事务B发生了回滚,此时事务A读取到的数据就是脏数据。
-
不可重复读。
事务A在多次读取某条记录,但是在此期间,事务B修改了相应的数据,使得事务A多次读取到的数据不一致,这就是不可重复读。解决不可重复读的问题是使用行锁
-
幻读。
事务A读取记录只有5条,但是事务B在此期间又插入了一条符合添加的数据,此时事务A读取到的记录就会多出来一条,就像出现幻觉一样,因此是幻读,幻读和不可重复读的区别是幻读侧重于删除或新增,而不可重复读侧重于修改,解决幻读的方法是使用锁表。
-
-
MySQL的事务隔离级别会产生的并发问题?
事务的隔离级别越高,数据库的并发性就越差,隔离级别由低到高如下,MySQL的默认隔离级别是可重复读。
-
读未提交
事务中的数据,即使没有提交,也会被其他事务读取到,会导致脏读、不可重复读、幻读。
-
读已提交
事务从开始到提交之间,数据对于其他事务都是不可见的,会导致不可重复读、幻读。
-
可重复读
一个事务按相同的查询条件读取以前检索过的数据,其他事务插入了满足其查询条件的新数据。会导致幻读。
可重复读可以通过手动加锁的方式解决幻读,如下:
select * from table_name where id = '1' for update;
以该方式获得悲观锁,禁止其他事务操作对应的数据,从而解决幻读问题。
底层是在索引上的next_key lock,因为InnoDB的行锁是添加到索引上的,索引映射的记录如果存在,加行锁,如果不存在,则会加 next-key lock / gap 锁 / 间隙锁,故InnoDB可以实现事务对某记录的预先占用,如果记录存在,它就是本事务的,如果记录不存在,那它也将是本是无的,只要本是无还在,其他事务就别想占有它。
-
串行化
事务串行执行,隔离级别最高,但是性能也最差。
-
-
MySQL的锁机制?
- 共享锁(读锁):不堵塞,多个用户可以同时读取一个资源,互不干扰;
- 排它锁(写锁):排它锁会阻塞其他的读锁和写锁,这样只允许一个用户进行写入,防止其他用户读取正在写入的数据。
-
锁的粒度?
- 表锁:系统开销最小,会锁定整张表,MyISAM使用的就是表锁;
- 行锁:最大程度的支持并发处理,但是也带来了最大的锁开销,InnoDB使用行锁。
-
什么是悲观锁?什么是乐观锁?
-
悲观锁
悲观锁是指对数据被外界修改持保守态度,因此在整个数据处理过程中,将数据处于锁定状态;
悲观锁的实现需要依靠数据库提供的锁机制,例如共享锁和排他锁都是悲观锁。
在悲观锁的情况下,为了保证事务的隔离性,就需要一致性锁定读。读取数据时加锁,其他事务无法修改这些数据,修改删除数据时也要加锁,其他事务无法读取这些数据。
悲观锁导致数据性能降低,特别是长事务的时候。
-
乐观锁
乐观锁实际就死基于版本号,通过CAS实现原子性更新。
-
-
什么是死锁?死锁发生的条件?如何降低死锁发生的概率?
死锁是多个进程访问同一个数据库时,其中每个进程拥有的锁都是其他进程所需要的,由此造成每个进程都无法继续下去。
死锁发生的条件:
- 互斥性:一个资源同一时间只能被一个进程持有;
- 请求和保持条件:一个进程持有一个资源不放,但是又要请求其他进程持有的资源;
- 不剥夺条件:进程已经持有的资源,除非自己释放,不然在使用完成之前不能被剥夺;
- 环路等待条件:p1等待p2的资源,p2等待p3的资源,p3等待p1的资源;
如何降低死锁发生概率:
- 设置获得锁的超时时间;
- 按同一顺序访问对象;
- 避免事务中的用户交互;
- 保持事务简短并在一个批处理中;
- 使用低隔离级别;
- 使用绑定链接;
-
MySQL 中 InnoDB 引擎的行锁是通过加在什么上完成(或称实现)的?为什么是这样子的??
InnoDB 是基于索引来完成行锁。
SELECT * FROM tab_with_index WHERE id = 1 FOR UPDATE
FOR UPDATE
可以根据条件来完成行锁锁定,并且 id 是有索引键的列,如果 id 不是索引键那么 InnoDB 将完成表锁,并发将无从谈起。 -
MySQL 查询执行顺序?
(7) SELECT (8) DISTINCT <select_list> (1) FROM <left_table> (3) <join_type> JOIN <right_table> (2) ON <join_condition> (4) WHERE <where_condition> (5) GROUP BY <group_by_list> (6) HAVING <having_condition> (9) ORDER BY <order_by_condition> (10) LIMIT <limit_number>
-
聊聊 MySQL SQL 优化?
数据库设计:尽量把数据库设计的占用磁盘空间更小
-
尽可能使用更小的数据类型;
-
尽可能定义字段为
not null
,除非这个字段必须为null
; -
如果没有用到边长字段,那么就采用固定大小的字段格式;
-
表的主索引应该尽可能的短;
-
只创建确实需要的索引;
系统的使用:
- 对查询进行优化,避免全表扫描,在
where
以及order by
涉及的字段添加索引; - 尽量避免在
where
子句中使用null
、is null
、<>
、OR
连接查询条件; - 使用
exist
代替in
; - 避免使用
%value%
查询; - 在
where
子句中使用参数,会导致全表扫描; - 尽量避免在
where
子句中使用函数或者表达式操作; - 使用复合索引时,要遵从最左匹配原则;
- 并不是所有的索引对查询都有效;
- 索引并不是越多越好,会降低写时性能且占用空间;
- 尽量使用具体的字段代替*;
- 尽量避免使用游标;
- 避免大事务操作,提高系统并发能力;
- 避免向客户端返回大量数据;
-
-
MySQL 数据库 CPU 飙升到 500% 的话,怎么处理?
- top命令确定是否是mysqld进程导致的;
- iostat确定哪个进程占用了磁盘IO;
- mysql中执行
show processlist
命令,查看session情况,找到高消耗的sql进行分析; - 或者查询mysql慢日志,查看是否有慢sql;
-
在 MySQL 服务器运行缓慢的情况下输入什么命令能缓解服务器压力?
-
检查系统的状态
- 使用
sar
命令检查操作系统磁盘的读写性能; - 使用
vmstat
监控内存、cpu资源; - 磁盘IO问题,处理方式:做raid10提高性能
- 网络问题,telnet一下mysql的对外开放端口是否开发,查看防火墙;
- 使用
-
检查mysql参数
- max_connect_errors
- connect_timeout
- skip-name-resolve
- slave-net-timeout=seconds
- master-connect-retry
-
检查mysql相关状态值
- 检查连接数
- 检查系统锁情况
- 检查慢查询日志
-
-
什么是MVCC?
多版本并发控制(MVCC),是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 这样在读操作不用阻塞写操作,写操作不用阻塞读操作的同时,避免了脏读和不可重复读.