1、大表优化的方式
https://zhuanlan.zhihu.com/p/147615129
2、B+树和B树
B树:所有节点都保存数据、查找性能不稳定
B+树:只有叶子节点保存数据、叶子节点用链表相连,所以支持范围查找
3、索引什么时候失效、索引优化
什么时候索引失效(避免索引失效,即为索引优化)
1)如果条件中有or,即使其中有部分条件带索引也不会使用(这也是为什么尽量少用or的原因)
2)复合索引未用左列字段(最左匹配原则)
3)like以%开头;
4)存在索引列的数据类型隐形转换,则用不上索引,比如列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引
5)where中索引列有运算;
6)where中索引列使用了函数;
7)如果mysql觉得全表扫描更快时(数据少);
什么时候没必要用索引
1)数据唯一性差(一个字段的取值只有几种时)
2)频繁更新的字段不用(更新索引消耗);
3)where中不用的字段;(只有在where语句出现,mysql才会去使用索引)
4)where 子句里对索引列使用不等于(<>),使用索引效果一般
查看sql有没有用索引:explain + sql
4、InnoDB和MyISAM对比
innodb支持:事务,异常恢复(redo),行级锁,外键
5、MySQL的隔离级别,各自解决的问题
RU,未提交读
RC,已提交读,解决脏读脏读
RR,可重复读,解决不可重复读(一条数据中的字段被修改)
SER,序列化,解决幻读(同一条语句查询出来的数据条数不一样)
RC(读已提交)和RR(可重复读)级别都用到了MVCC来进行不加锁的读,但是为什么RR级别可以解决幻读,对于RC级别不行?
6、MySQL性能优化
三方面:1)sql/索引优化 2)数据库结构优化 3)硬件升级
1)sql/索引优化
(1) 查询具体的字段而非全部字段 任何地方都不要使用select * from t
(2) 优化子查询,尽量使用 Join 语句来替代子查询,因为子查询是嵌套查询,而嵌套查询会新创建一张临时表,而临时表的创建与销毁会占用一定的系统资源以及花费一定的时间,但 Join 语句并不会创建临时表,因此性能会更高。
(3)尽量使用小表驱动大表(注意查询结果集)
我们要尽量使用小表驱动大表的方式进行查询,也就是如果 B 表的数据小于 A 表的数据,那执行的顺序就是先查 B 表再查 A 表
(4) 适当增加冗余字段
增加冗余字段可以减少大量的连表查询,因为多张表的连表查询性能很低,所有可以适当的增加冗余字段,以减少多张表的关联查询,这是以空间换时间的优化策略。
(5) 当只要一行数据时使用limit 1
查询时如果已知会得到一条数据,这种情况下加上limit 1会增加性能。因为MySQL数据库引擎会在找到一条结果停止搜索,而不是继续查询下一条是否符合标准直到所有记录查询完毕。
(6)对于连续数值,使用BETWEEN不用IN,in可能会索引失效
使用in时,当IN的取值范围较大时可能会导致索引失效,走全表扫描
(7) 排查慢SQL
慢查询:超过指定时间的SQL语句查询称为“慢查询”。
慢查询通常的排查手段是先使用慢查询日志功能,查询出比较慢的 SQL 语句,然后再通过 explain 来查询 SQL 语句的执行计划,最后分析并定位出问题的根源,再进行处理。
2)数据库结构优化
① 表的字段长度尽可能小
一般说来数据库的表越小,那么它的查询速度就越快,因此为了提高表的效率,应该将表的字段设置的尽可能小,比如身份证号,可以设置为 char(18) 就不要设置为 varchar(18)。
② 使用最简单数据类型
能使用 int 类型就不要使用 varchar 类型,因为 int 类型比 varchar 类型的查询效率更高。
③ 尽量少定义 text 类型
text 类型的查询效率很低,如果必须要使用 text 定义字段,可以把此字段分离成子表,需要查询此字段时使用联合查询,这样可以提高主表的查询效率。
④ 适当分表、分库策略
分表和分库方案也是我们经常说的垂直分隔(分表)和水平分隔(分库)。
分表是指当一张表中的字段更多时,可以尝试将一张大表拆分为多张子表,把使用比较高频的主信息放入主表中,其他的放入子表,这样我们大部分查询只需要查询字段更少的主表就可以完成了,从而有效的提高了查询的效率。
分库是指将一个数据库分为多个数据库。比如我们把一个数据库拆分为了多个数据库,一个主数据库用于写入和修改数据,其他的用于同步主数据并提供给客户端查询,这样就把一个库的读和写的压力,分摊给了多个库,从而提高了数据库整体的运行效率。
3)硬件升级
7、回表
先定位主键值,再定位行记录
InnoDB 存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次,这样就会比较慢。覆盖索引就是把要查询出的列和索引是对应的,不做回表操作
将被查询的字段,建立到联合索引里去(或者说 查询的字段都已经建立了索引)
8、主键和唯一索引的区别
主键是一种约束,唯一索引是一种索引,两者在本质上是不同的。
主键创建后一定包含一个唯一性索引,唯一性索引并不一定就是主键。
唯一性索引列允许空值,而主键列不允许为空值。
主键列在创建时,已经默认为空值 + 唯一索引了。
主键可以被其他表引用为外键,而唯一索引不能。
一个表最多只能创建一个主键,但可以创建多个唯一索引。
主键更适合那些不容易更改的唯一标识,如自动递增列、身份证号等。
在 RBO 模式下,主键的执行计划优先级要高于唯一索引。 两者可以提高查询的速度。
9、幻读的解决方法
1)MVCC 多版本控制
2)使用 next-key lock (行锁+间隙锁)
10、数据库死锁
通过执行show engine innodb status 可以查看到最近一次死锁的日志
合理的设计索引,区分度高的列放到组合索引前面,使业务 SQL 尽可能通过索引定位更少的行,减少锁竞争。
调整业务逻辑 SQL 执行顺序, 避免 update/delete 长时间持有锁的 SQL 在事务前面。
避免大事务,尽量将大事务拆成多个小事务来处理,小事务发生锁冲突的几率也更小。
以固定的顺序访问表和行。比如两个更新数据的事务,事务 A 更新数据的顺序为 1,2;事务 B 更新数据的顺序为 2,1。这样更可能会造成死锁。
在并发比较高的系统中,不要显式加锁,特别是是在事务里显式加锁。如 select … for update 语句,如果是在事务里(运行了 start transaction 或设置了autocommit 等于0),那么就会锁定所查找到的记录。
尽量按主键/索引去查找记录,范围查找增加了锁冲突的可能性,也不要利用数据库做一些额外额度计算工作。比如有的程序会用到 “select … where … order by rand();”这样的语句,由于类似这样的语句用不到索引,因此将导致整个表的数据都被锁住。
优化 SQL 和表设计,减少同时占用太多资源的情况。比如说,减少连接的表,将复杂 SQL 分解为多个简单的 SQL。
Innodb解决死锁:回滚掉持有最少行级锁的事务
1)一张表两行记录交叉申请互斥锁
2)两张表两行记录交叉申请互斥锁
3)聚集索引与非聚集索引冲突
4)聚集索引冲突
5)间隙锁冲突
11、数据库自增ID
单机实现:
数据库自增ID和mysql数据库的replace_into()函数实现的,这里的replace into跟insert功能类似,不同点在于:replace into首先尝试插入数据列表中,如果发现表中已经有此行数据(根据主键或唯一索引判断)则先删除,再插入。否则直接插入新数据。
分布式id在数据库里面的实现过程:
既然是分布式id,那么最少要使用两个数据库,这里我们使用3台来讲解,为了保证每一台数据库里面的id自增的时候不会重复,那么我们就要给每一台数据库设置auto-increment-increment和auto-increment-offset这两个属性值(auto-increment-increment表示每一台数据库的起始id值,然后auto-increment-offset表示每一台数据库每一次的增加数字)
如果我们有n台数据库的话,那么上面的auto-increment-increment和auto-increment-offset这两个属性值应该怎么设计呢,我们给每一台数据库设置初始值分别为1,2,3...N,然后每一台数据库自增步长为机器的台数N
12、索引更新的过程
13、mybatis 一级缓存、二级缓存
一级缓存又被称为 SqlSession 级别的缓存,二级缓存又被称为表级缓存
一级缓存:
SqlSession 是SqlSessionFactory会话工厂创建出来的一个会话的对象,这个SqlSession对象用于执行具体的SQL语句并返回给用户请求的结果。
SqlSession级别的缓存表示的就是每当执行一条SQL语句后,默认就会把该SQL语句缓存起来,也被称为会话缓存
插入语句会使一级缓存失效,使用 sqlSession 的 clearCache()也可以使一级缓存失效
二级缓存:
如果多个 SqlSession 需要共享缓存,则需要开启二级缓存
14、MySQL explain详解
https://cloud.tencent.com/developer/article/1093229
type:这一列表示关联类型或访问类型,即MySQL决定如何查找表中的行。
依次从最优到最差分别为:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
ALL:即全表扫描,意味着mysql需要从头到尾去查找所需要的行。通常情况下这需要增加索引来进行优化了
extra:这一列展示的是额外信息。
distinct: 一旦mysql找到了与行相联合匹配的行,就不再搜索了
Using index:这发生在对表的请求列都是同一索引的部分的时候,返回的列数据只使用了索引中的信息,而没有再去访问表中的行记录。是性能高的表现。
Using where:mysql服务器将在存储引擎检索行后再进行过滤。就是先读取整行数据,再按 where 条件进行检查,符合就留下,不符合就丢弃。
Using temporary:mysql需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的,首先是想到用索引来优化。
Using filesort:mysql 会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。此时mysql会根据联接类型浏览所有符合条件的记录,并保存排序关键字和行指针,然后排序关键字并按顺序检索行信息。这种情况下一般也是要考虑使用索引来优化的。
15、MySQL删除表操作(delete、truncate、drop的区别)
https://zhuanlan.zhihu.com/p/270331768
可以这么理解,一本书,delete是把目录撕了,truncate是把书的内容撕下来烧了,drop是把书烧了
16、缓存和数据库的一致性?延时双删具体如何实现?
更新缓存的的Design Pattern有四种:Cache aside, Read through, Write through, Write behind caching
延时双删:消息队列,重试机制
缺点:侵入性太强
改进:用非业务代码,订阅binlog,删除缓存
17、MySQL的几种log
MySQL 中有六种日志文件,分别是:
重做日志(redo log):事务持久性
回滚日志(undo log):事务原子性,回滚
二进制日志(binlog):主从一致,只记录修改命令
错误日志(errorlog)
慢查询日志(slow query log)
一般查询日志(general log)
中继日志(relay log)