1. MySQL 索引使用有哪些注意事项呢?
可以从两个维度回答这个问题:索引哪些情况会失效,索引不适合哪些场景
 
索引哪些情况会失效
查询条件包含 or,会导致索引失效。
隐式类型转换,会导致索引失效,例如 age 字段类型是 int,我们 where age = “1”,这样就会触发隐式类型转换。
like 通配符会导致索引失效,注意:”ABC%” 不会失效,会走 range 索引,”% ABC” 索引会失效
联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。
对索引字段进行函数运算。
对索引列运算(如,+、-、*、/),索引失效。
索引字段上使用(!= 或者 < >,not in)时,会导致索引失效。
索引字段上使用 is null, is not null,可能导致索引失效。
相 join 的两个表的字符编码不同,不能命中索引,会导致笛卡尔积的循环计算
mysql 估计使用全表扫描要比使用索引快,则不使用索引。
索引不适合哪些场景
数据量少的不适合加索引
更新比较频繁的也不适合加索引
离散性低的字段不适合加索引(如性别)
2. MySQL 遇到过死锁问题吗,你是如何解决的?
排查死锁的步骤:
 
查看死锁日志 show engine innodb status;
找出死锁 Sql
分析 sql 加锁情况
模拟死锁案发
分析死锁日志
分析死锁结果
3. 日常工作中你是怎么优化 SQL 的?
可以从这几个维度回答这个问题:
 
加索引
避免返回不必要的数据
适当分批量进行
优化 sql 结构
主从架构,提升读性能
分库分表
4. 分库分表的设计
分库分表方案,分库分表中间件,分库分表可能遇到的问题
 
分库分表方案
水平分库:以字段为依据,按照一定策略(hash、range 等),将一个库中的数据拆分到多个库中。
水平分表:以字段为依据,按照一定策略(hash、range 等),将一个表中的数据拆分到多个表中。
垂直分库:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。
垂直分表:以字段为依据,按照字段的活跃性,将表中字段拆到不同的表(主表和扩展表)中。
常用的分库分表中间件
sharding-jdbc
Mycat
分库分表可能遇到的问题
事务问题:需要用分布式事务啦
跨节点 Join 的问题:解决这一问题可以分两次查询实现
跨节点的 count,order by,group by 以及聚合函数问题:分别在各个节点上得到结果后在应用程序端进行合并。
数据迁移,容量规划,扩容等问题
ID 问题:数据库被切分后,不能再依赖数据库自身的主键生成机制啦,最简单可以考虑 UUID
跨分片的排序分页问题(后台加大 pagesize 处理?)
5. InnoDB 与 MyISAM 的区别
InnoDB 支持事务,MyISAM 不支持事务
InnoDB 支持外键,MyISAM 不支持外键
InnoDB 支持 MVCC (多版本并发控制),MyISAM 不支持
select count (*) from table 时,MyISAM 更快,因为它有一个变量保存了整个表的总行数,可以直接读取,InnoDB 就需要全表扫描。
Innodb 不支持全文索引,而 MyISAM 支持全文索引(5.7 以后的 InnoDB 也支持全文索引)
InnoDB 支持表、行级锁,而 MyISAM 支持表级锁。
InnoDB 表必须有主键,而 MyISAM 可以没有主键
Innodb 表需要更多的内存和存储,而 MyISAM 可被压缩,存储空间较小,。
Innodb 按主键大小有序插入,MyISAM 记录插入顺序是,按记录插入顺序保存。
InnoDB 存储引擎提供了具有提交、回滚、崩溃恢复能力的事务安全,与 MyISAM 比 InnoDB 写的效率差一些,并且会占用更多的磁盘空间以保留数据和索引
InnoDB 属于索引组织表,使用共享表空间和多表空间储存数据。MyISAM 用.frm、.MYD、.MTI 来储存表定义,数据和索引。
6. 数据库索引的原理,为什么要用 B + 树,为什么不用二叉树?
可以从几个维度去看这个问题,查询是否够快,效率是否稳定,存储数据多少,以及查找磁盘次数,为什么不是二叉树,为什么不是平衡二叉树,为什么不是 B 树,而偏偏是 B + 树呢?
 
为什么不是一般二叉树?
1)当数据量大时,树的高度会比较高(树的高度决定着它的 IO 操作次数,IO 操作耗时大),查询会比较慢。 2)每个磁盘块(节点 / 页)保存的数据太小(IO 本来是耗时操作,每次 IO 只能读取到一个关键字,显然不合适),没有很好的利用操作磁盘 IO 的数据交换特性,也没有利用好磁盘 IO 的预读能力(空间局部性原理),从而带来频繁的 IO 操作。
 
为什么不是平衡二叉树呢?
我们知道,在内存比在磁盘的数据,查询效率快得多。如果树这种数据结构作为索引,那我们每查找一次数据就需要从磁盘中读取一个节点,也就是我们说的一个磁盘块,但是平衡二叉树可是每个节点只存储一个键值和数据的,如果是 B 树,可以存储更多的节点数据,树的高度也会降低,因此读取磁盘的次数就降下来啦,查询效率就快啦。
 
那为什么不是 B 树而是 B + 树呢?
1)B+Tree 范围查找,定位 min 与 max 之后,中间叶子节点,就是结果集,不用中序回溯 2)B+Tree 磁盘读写能力更强(叶子节点不保存真实数据,因此一个磁盘块能保存的关键字更多,因此每次加载的关键字越多) 3)B+Tree 扫表和扫库能力更强(B-Tree 树需要扫描整颗树,B+Tree 树只需要扫描叶子节点)
 
详细参考:索引原理
 
7. 聚集索引与非聚集索引的区别
一个表中只能拥有一个聚集索引,而非聚集索引一个表可以存在多个。
聚集索引,索引中键值的逻辑顺序决定了表中相应行的物理顺序;非聚集索引,索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同。
索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。
聚集索引:物理存储按照索引排序;非聚集索引:物理存储不按照索引排序;
何时使用聚集索引或非聚集索引?
 
 
8. limit 1000000 加载很慢的话,你是怎么解决的呢?
方案一:如果 id 是连续的,可以这样,返回上次查询的最大记录 (偏移量),再往下 limit
 
select id,name from employee where id>1000000 limit 10.
复制代码
方案二:在业务允许的情况下限制页数: 建议跟业务讨论,有没有必要查这么后的分页啦。因为绝大多数用户都不会往后翻太多页。
 
方案三:order by + 索引(id 为索引)
 
select id,name from employee order by id  limit 1000000,10
SELECT a.* FROM employee a, (select id from employee where 条件 LIMIT 1000000,10 ) b where a.id=b.id
复制代码
方案四:利用延迟关联或者子查询优化超多分页场景。(先快速定位需要获取的 id 段,然后再关联)
 
9. 如何选择合适的分布式主键方案呢?
数据库自增长序列或字段。
UUID
雪花算法
Redis 生成 ID
利用 zookeeper 生成唯一 ID
10. 事务的隔离级别有哪些?MySQL 的默认隔离级别是什么?
什么是事务的隔离性?
 
隔离性是指,多个用户的并发事务访问同一个数据库时,一个用户的事务不应该被其他用户的事务干扰,多个并发事务之间要相互隔离。
 
咱们举例子来说明:
 
建表语句:
 
CREATE TABLE `T`  (
  `id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE = INNODB;
复制代码
数据列表:
 
id name
1 xiaohong
2 zhangsan
3 lisi
案例一:
 
事务 A,先执行,处于未提交的状态:
 
insert into T values(4, wangwu); 
复制代码
事务 B,后执行,也未提交:
 
select * from T; 
复制代码
如果事务 B 能够读取到 (4, wangwu) 这条记录,事务 A 就对事务 B 产生了影响,这个影响叫做 “读脏”,读到了未提交事务操作的记录。
 
案例二:
 
事务 A,先执行:
 
select * from T where id=1; 
复制代码
结果集为:1, xiaohong
 
事务 B,后执行,并且提交:
 
update T set name=hzy where id=1; 
复制代码
commit;
 
事务 A,再次执行相同的查询:
 
select * from T where id=1; 
复制代码
结果集为:1, hzy
 
这次是已提交事务 B 对事务 A 产生的影响,这个影响叫做 “不可重复读”,一个事务内相同的查询,得到了不同的结果。
 
案例三:
 
事务 A,先执行:
 
select * from T where id>3; 
复制代码
结果集为: NULL
 
事务 B,后执行,并且提交:
 
insert into T values(4, wangwu); 
commit; 
复制代码
事务 A,首次查询了 id>3 的结果为 NULL,于是想插入一条为 4 的记录:
 
insert into T values(4, hzy); 
复制代码
结果集为: Error : duplicate key!
 
这次是已提交事务 B 对事务 A 产生的影响,这个影响叫做 “幻读”。
 
可以看到,并发的事务可能导致其他事务:
 
读脏
 
不可重复读
 
幻读
 
InnoDB 实现了四种不同事务的隔离级别:
 
读未提交 (Read Uncommitted)
读提交 (Read Committed, RC)
可重复读 (Repeated Read, RR)
串行化 (Serializable)
不同事务的隔离级别,实际上是一致性与并发性的一个权衡与折衷。
 
InnoDB 的四种事务的隔离级别,分别是怎么实现的?
 
InnoDB 使用不同的锁策略 (Locking Strategy) 来实现不同的隔离级别。
 
读未提交 (Read Uncommitted)
这种事务隔离级别下,select 语句不加锁。
 
此时,可能读取到不一致的数据,即 “读脏”。这是并发最高,一致性最差的隔离级别。
 
串行化 (Serializable)
这种事务的隔离级别下,所有 select 语句都会被隐式的转化为 select … in share mode.
 
这可能导致,如果有未提交的事务正在修改某些行,所有读取这些行的 select 都会被阻塞住。
 
这是一致性最好的,但并发性最差的隔离级别。 在互联网大数据量,高并发量的场景下,几乎不会使用上述两种隔离级别。
 
可重复读 (Repeated Read, RR) 这是 InnoDB 默认的隔离级别,在 RR 下:
①普通的 select 使用快照读 (snapshot read),这是一种不加锁的一致性读 (Consistent Nonlocking Read),底层使用 MVCC 来实现;
 
②加锁的 select (select … in share mode /select … for update), update, delete 等语句,它们的锁,依赖于它们是否在唯一索引 (unique index) 上使用了唯一的查询条件 (unique search condition),或者范围查询条件 (range-type search condition):
 
在唯一索引上使用唯一的查询条件,会使用记录锁 (record lock),而不会封锁记录之间的间隔,即不会使用间隙锁 (gap lock) 与临键锁 (next-key lock)
范围查询条件,会使用间隙锁与临键锁,锁住索引记录之间的范围,避免范围间插入记录,以避免产生幻影行记录,以及避免不可重复的读
读提交 (Read Committed, RC) 这是互联网最常用的隔离级别,在 RC 下:
①普通读是快照读;
 
②加锁的 select, update, delete 等语句,除了在外键约束检查 (foreign-key constraint checking) 以及重复键检查 (duplicate-key checking) 时会封锁区间,其他时刻都只使用记录锁;
 
此时,其他事务的插入依然可以执行,就可能导致,读取到幻影记录。