MySQL 遇到过死锁问题吗,你是如何解决的?
排查死锁的步骤:
- 查看死锁日志show engine innodb status;
- 找出死锁Sql
- 分析sql加锁情况
- 模拟死锁案发
- 分析死锁日志
- 分析死锁结果
数据库索引的原理,为什么要用 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树只需要扫描叶子节点)
聚集索引与非聚集索引的区别
一个表中只能拥有一个聚集索引,而非聚集索引一个表可以存在多个。
聚集索引,索引中键值的逻辑顺序决定了表中相应行的物理顺序;非聚集索引,索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同。
索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。
聚集索引:物理存储按照索引排序;非聚集索引:物理存储不按照索引排序;
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段,然后再关联)
如何选择合适的分布式主键方案呢?
数据库自增长序列或字段。
UUID
雪花算法
Redis生成ID
利用zookeeper生成唯一ID
在高并发情况下,如何做到安全的修改同一行数据?
要安全的修改同一行数据,就要保证一个线程在修改时其它线程无法更新这行记录。一般有悲观锁和乐观锁两种方案
使用悲观锁
悲观锁思想就是,当前线程要进来修改数据时,别的线程都得拒之门外~ 比如,可以使用select…for update
select * from User where name=‘jay’ for update
以上这条sql语句会锁定了User表中所有符合检索条件(name=‘jay’)的记录。本次事务提交之前,别的线程都无法修改这些记录。
使用乐观锁
乐观锁思想就是,有线程过来,先放过去修改,如果看到别的线程没修改过,就可以修改成功,如果别的线程修改过,就修改失败或者重试。实现方式:乐观锁一般会使用版本号机制或CAS算法实现。
数据库的乐观锁和悲观锁
悲观锁
悲观锁她专一且缺乏安全感了,她的心只属于当前事务,每时每刻都担心着它心爱的数据可能被别的事务修改,所以一个事务拥有(获得)悲观锁后,其他任何事务都不能对数据进行修改啦,只能等待锁被释放才可以执行。
乐观锁
乐观锁的“乐观情绪”体现在,它认为数据的变动不会太频繁。因此,它允许多个事务同时对数据进行变动。实现方式:乐观锁一般会使用版本号机制或CAS算法实现。
SQL优化的一般步骤是什么,怎么看执行计划(explain),如何理解其中各个字段的含义?
将explain加在需要查看的sql语句前面然后执行
如:explain select * from user;
id:
查询中 SELECT 的标识符。如果你的查询包含子查询或 UNION,MySQL 会为每个 SELECT 语句分配一个唯一的 ID。
对于简单查询,通常只有一个 SELECT,其 id 为 1。
对于复杂的查询,子查询的 id 会递增。
select_type:
查询的类型,表示这个 SELECT 语句是简单查询、复杂查询的一部分,还是 UNION 的一部分等。
常见的值有:SIMPLE(简单 SELECT,不使用 UNION 或子查询)、PRIMARY(查询中最外层的 SELECT)、UNION(UNION 中的第二个或后续的 SELECT 语句)、DEPENDENT UNION(UNION 中的第二个或后续的 SELECT,依赖于外部查询)、SUBQUERY(子查询中的第一个 SELECT)、DEPENDENT SUBQUERY(子查询,依赖于外部查询)等。
table:
显示这一行查询涉及哪个表。
partitions:
匹配查询的分区。如果表是分区表,这里会显示哪些分区被查询命中。
type:
连接类型或访问类型,表示 MySQL 在找到所需行时如何查找表中的数据。常见的类型包括:ALL(全表扫描)、index(全索引扫描)、range(索引范围扫描)、ref(非唯一性索引扫描)、eq_ref(唯一性索引扫描,对于每个索引键,表中最多只有一条匹配行)、const/system(表中最多有一个匹配行,例如主键或唯一索引扫描)等。
type 列的值越优(例如 eq_ref、const > ref > range > index > ALL),查询性能越好。
possible_keys:
显示查询中可能使用的索引。
key:
实际使用的索引。如果没有使用索引,则为 NULL。
key_len:
使用的索引的长度。在一些情况下,不是索引的全部部分都会被使用。
ref:
显示索引的哪一列或常量被用于查找值。
rows:
MySQL 估计为了找到所需的行而要检查的行数。这是一个估计值,并不总是完全准确,但在优化查询时很有参考价值。
filtered:
表示返回结果的行占开始查找行的百分比。
Extra:
包含不适合在其他列中显示的额外信息。例如:是否使用了文件排序(Using filesort)、是否使用了临时表(Using temporary)等。
常见的值包括:
Using where:表示存储引擎在返回结果前应用了 WHERE 条件。
Using temporary:表示 MySQL 需要创建一个临时表来存储结果。
Using filesort:表示 MySQL 需要对数据进行额外的排序操作,不能通过索引顺序获得结果。
No tables used:没有使用表(例如,查询中只包含常量)
select for update有什么含义,会锁表还是锁行还是其他?
select for update 含义
select查询语句是不会加锁的,但是select for update除了有查询的作用外,还会加锁呢,而且它是悲观锁哦。至于加了是行锁还是表锁,这就要看是不是用了索引/主键啦。 没用索引/主键的话就是表锁,否则就是是行锁。
如果某个表有近千万数据,CRUD比较慢,如何优化?
分库分表
某个表有近千万数据,可以考虑优化表结构,分表(水平分表,垂直分表),当然,你这样回答,需要准备好面试官问你的分库分表相关问题呀,如
分表方案(水平分表,垂直分表,切分规则hash等)
分库分表中间件(Mycat,sharding-jdbc等)
分库分表一些问题(事务问题?跨节点Join的问题)
解决方案(分布式事务等)
索引优化
除了分库分表,优化表结构,当然还有所以索引优化等方案~
如何写sql能够有效的使用到复合索引?
复合索引,也叫组合索引,用户可以在多个列上建立索引,这种索引叫做复合索引。
当我们创建一个组合索引的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,这就是最左匹配原则。
select * from table where k1=A AND k2=B AND k3=D
有关于复合索引,我们需要关注查询Sql条件的顺序,确保最左匹配原则有效,同时可以删除不必要的冗余索引。