Java面试_数据库篇_优化,事务,Mysql
- 优化
- 如何定位慢查询
- 方案一: 开源工具
- 方案二: Mysql自带慢日志
- 如何分析慢SQL语句
- 索引
- 介绍索引
- 聚簇索引和非聚簇索引,回表查询
- 覆盖索引,超大分页优化
- 索引创建的原则
- 索引失效
- 谈谈sql优化的经验
- 事务
- 事务特性
- 隔离级别
- undo log和redo log
- MVCC
- 主从同步原理
- 分库分表
- 来源
优化
如何定位慢查询
方案一: 开源工具
- 调试工具: Arthas
- 运维工具: Prometheus, Skywalking
方案二: Mysql自带慢日志
在Mysql的配置文件(/etc/my.cnf
)中配置:
# 开启MySQL慢日志查询开关
slow_query_log=1
# 设置慢日志的时间为2秒, SQL语句执行时间超过2秒, 就会视为慢查询, 记录慢查询日志
long_query_time=2
如何分析慢SQL语句
explain select * from ap_user where id = 1
- possible_key: 当前sql可能会使用到的索引
- key: 当前sql实际命中的索引
- key_len: 索引占用的大小
- Extra: 额外的优化建议
Extra 含义 Using where; Using Index 查找使用了索引, 需要的数据都在索引列中能找到, 不需要回表查询数据 Using index condition 查找使用了索引, 但是需要回表查询数据 - type: 这条sql的连接类型, 性能从好到差
- NULL: 没有使用表
- system: 查询系统中的表
- const: 根据主键查询
- eq_ref: 主键索引查询或唯一索引查询
- ref: 索引查询
- range: 索引范围查询
- index: 索引树扫描, 需要优化
- all: 全盘扫描, 需要优化
使用MySQL自带的分析工具 EXPLAIN(DESC)
- 通过key和key_len检查是否命中了索引(如果索引本身存在, 则可能存在索引失效的情况)
- 通过type字段查看sql是否有进一步的优化空间, 是否存在全索引扫描或全盘扫描
- 通过extra建议判断, 是否出现了回表的情况, 如果出现了, 可以尝试添加索引或修改返回字段来修复
索引
介绍索引
- 什么是索引
- 索引(index)是帮助Mysql高效获取数据的数据结构(有序)
- 提高数据检索的效率,降低数据库的IO成本(不需要全表扫描)
- 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
- 索引的底层数据结构
- Mysql的InnoDB引擎采用的B+树的数据结构来存储索引
- 阶数更多,路径更短
- 磁盘读写代价B+树更低,非叶子节点只存储指针,叶子节点存储数据
- B+树便于扫库和区间查询,叶子节点间是双向链表
聚簇索引和非聚簇索引,回表查询
- 聚簇索引(聚集索引):数据与索引放到一块,B+树的叶子节点保存了整行数据,有且只有一个
- 非聚簇索引(二级索引):数据与索引分开存储,B+树的叶子节点保存对应的主键,可以有多个
- 回表查询:通过二级索引找到对应的主键值,到聚集索引中查找整行数据,这个过程就是回表
- extend: 聚集索引选取规则
- 如果存在主键,主键索引就是聚集索引
- 如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引
- 如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引
覆盖索引,超大分页优化
- 什么是覆盖索引
- 覆盖索引:指查询使用了索引,返回的列,必须在索引中全部能够找到
- 如果返回的列中没有创建索引,有可能会触发回表查询,尽量避免使用select *
- 超大分页优化
- 当数据量比较大时,limit分页查询,需要对数据进行排序,效率低
- 解决方案:覆盖索引+子查询
索引创建的原则
- 数据量较大,且查询比较频繁的表(10w)
- 常作为查询条件、排序、分组的字段
- 尽量选择内容区分度高的字段,尽量使用唯一索引
- 内容较长,使用前缀索引
- 尽量使用联合索引,避免回表
- 要控制索引的数量,加大增删改的成本
- 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它,可以让优化器更好地选择索引
索引失效
- 联合查询,违反最左前缀法则
- 范围查询右边的列,不能使用索引
- 不要在索引列上进行运算操作,索引将失效(substring)
- 字符串不加单引号,造成索引失效(类型转换)
- 以%开头的like模糊查询,索引失效
谈谈sql优化的经验
- 表的设计优化
- 设置合适的数值(tinyint int bigint), 根据实际情况选择
- 设置合适的字符串类型(char varchar), char定长效率高, varchar可变长度, 效率稍低
- SQL语句优化
- select语句务必指明字段名称(避免直接使用 select *)
- sql语句要避免造成索引失效的写法
- 尽量用union all代替union,union会多一次过滤,效率低
- 避免在where子句中对字段进行表达式操作
- Join优化,能用inner join就不用left join,right join,如果需要使用则要以小表为驱动。内连接会对两个表进行优化,优先把小表放到外边,大表放到里边。left join或right join不会重新调整顺序。
- 主从复制,读写分离,不让数据的写入影响都操作
- 分库分表
事务
事务特性
- 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
- 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
- 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
- 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
- 参考回答
隔离级别
-
并发事务的问题
-
隔离级别
-
参考回答
undo log和redo log
- 缓冲池(buffer pool):主内存中的一个区域,里面可以换成磁盘上经常操作的真实数据,在执行增删改查操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓冲),以一定频率刷新到磁盘,从而减少磁盘io,加快处理速度。
- 数据页(page):是InnoDB存储引擎磁盘管理的最小单元,每个页的大小默认为16KB。页中存储的是行数据。
- redo log:记录的是数据页的物理变化,服务宕机可用来同步数据
- undo log:记录的是逻辑日志,当事务回滚时,通过逆操作恢复原来的数据
- redo log保证了事务的持久性,undo log保证了事务的原子性和一致性
- 参考回答
MVCC
MVCC是Mysql中的多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突。
实现原理如下:
- 隐藏字段
- trx_id(事务id),记录每一次操作的事务id,是自增的。
- roll_pointer(回滚指针),指向上一个版本的事务版本记录地址。
- undo log
- 回滚日志,存储老版本数据
- 版本链:多个事务并行操作某一行记录,记录不同事务修改数据的版本,通过roll_pointer指针形成一个链表。
- readView解决的是一个事务查询选择版本的问题
- 根据readView的匹配规则和当前的一些事务id判断该访问哪个版本的数据。
- 不同的隔离级别快照读是不一样的,最终的访问的结果不一样
- RC:每一次执行快照读时生成ReadView
- RR:仅在事务中第一次执行快照读时生成ReadView,后续复用。
主从同步原理
分库分表
在数据量达到单表1000万或超过20G,就可以考虑分库分表
具体拆分策略如下:
- 水平分库:将一个库的数据拆分到多个库中,解决海量数据存储和高并发的问题
- 水平分表:解决单表存储和性能的问题
- 垂直分库:根据业务进行拆分,高并发下提高磁盘IO和网络连接数
- 垂直分表:冷热数据分离,多表互不影响。
来源
黑马程序员. 新版Java面试专题视频教程