数据库拾遗

  • 本文整理自剑指Java面试-Offer直通车

  • 如何设计一个关系型数据库

    • 存储

      • 文件系统

    • 程序实例

      • 存储管理

        • IO效率极低,所以数据库中的行在存储中没有实际意义,一般都是按块或页存储,一次IO读取多行数据

      • 缓存机制

        • 一旦某行数据被访问了,它周围的数据也极有可能被访问

        • 缓存不宜过大,要有淘汰机制

      • SQL解析

        • 将SQL编译解析,翻译成机器可识别的指令

        • 缓存

      • 日志管理

      • 权限划分

        • 多用户管理

      • 容灾机制

        • 从错误中恢复

      • 索引管理

      • 锁管理

  • 为什么要使用索引

    • 如果不使用索引,数据库会进行全表扫描,使用索引是为了尽量避免全表扫描,加快查询速度

      • 全表扫描在数据量少的表中,查询效率反而更快

      • 索引的灵感来自于字典

  • 什么样的信息能成为索引

    • 能把该记录限定在一定查找范围内的字段,即关键信息,如主键

    • 主键、唯一键以及普通键等

  • 索引的数据结构

    • 平衡二叉树(不适合)

      • 平衡二叉树查找效率为O(logn)

        • 检索深度每加一,都要进行一次IO,严重影响查询效率

      • 数据库的数据经过增删后,很容易变成线性结构,查询效率为O(n)

        • 可通过树的旋转特性,使树保持为平衡二叉树 

    • B-Tree(平衡多路查找树,不适合)

      • 为了减少IO,需要把平衡二叉树变矮,使每个节点存储的数据变多,则成为了B-Tree

      • 查找效率为O(logn)

      • 与平衡二叉树相比,B-Tree可以通过合并、上移下移节点等保持结构

    • B+-Tree(适合,常用)

      • 是对B-Tree优化后的一种数据类型,所有数据存在叶子节点中,非叶子节点只存储关键字,会比B-Tree更矮

      • 叶子节点有链指针指向下一个叶子节点,可横向做统计

      • B+-Tree更适合来做存储索引

        • 磁盘读写代价更低(数据都在叶子节点中,一次读取的索引更多)

        • 查找效率更加稳定(非叶子节点只存索引,所有数据走的路径一样,效率O(logn))

        • 有利于数据库的扫描(链指针)

    • Hash(小众)

      • 缺点

        • 仅能满足“=”,“IN”,不能使用范围查询

        • 无法被用来避免数据的排序操作

        • 不能利用部分索引建查询(组合索引中的某个索引,B+-Tree支持)

        • 不能避免表扫描

        • 遇到大量Hash值相等的情况后,性能并不一定比B-Tree索引高

    • BitMap(位图)索引

      • Oracle支持,MySQL不支持

      • 只有几种值的情况(如性别),效率很高

      • 新增修改的时候,通常会把同一个位图的数据都锁住

  • 密集索引和稀疏索引的区别

    • 密集索引文件中的每个搜索码值都对应一个索引值

      • 叶子节点保存的不止是键值,还保存了位于同一行的其他列的信息

      • 由于密集索引决定了表的物理排列顺序,一个表只能有一个密集索引

    • 稀疏索引文件只为索引码的某些值建立索引项

      • 叶子节点仅保存了键位信息以及该行数据的地址

      • 有的稀疏索引仅保存了键位信息及其主键

      • 定位到叶子节点后,还需要通过其中的信息,进一步定位到数据

    • MySQL中的InnoDB(聚簇)

      • 必须定义一个密集索引

        • 若一个逐渐被定义,则该主键座位密集索引

        • 若没有主键被定义,则该表的第一个唯一非空索引作为密集索引

        • 若不满足以上条件,InnoDB内部会生成一个隐藏主键(密集索引)

    • MySQL中的MyISAM(非聚簇)

      • 使用了稀疏索引

  • 如何定位并优化慢SQL

    • 根据慢日志定位慢查询SQL 

      • show variables like '%query%'

        • slow_query_log

          • set global slow_query_log = ON

        • slow_query_log_file

          • 慢SQL日志地址,慢SQL会写入此中

        • long_query_time

          • 查询时间大于此值,则属于慢SQL

          • set global long_query_time = 1

            • 默认10秒太长

            • 需要重新连接客户端

      • show status like '%slow_queries%'

        • 本次回话的慢查询执行次数

    • 使用explain等工具分析SQL

      • 直接加在查询语句前面

      • type,从最优到最差

        • system,const,eq_ref,ref,fulltext,ref_or_null,index_merge,unique_subquery,index_subquery,range,index,all

          • 一般情况下,如果是index和all,则表示语句需要优化

      • extra,出现以下2项意味着MySQL根本不能使用索引,效率会受到重大影响,有可能需要优化语句

        • Using filesort

          • 表示MySQL会对结果使用一个外部索引排序,而不是从表里按索引次序读到相关内容

          • 可能在内存或者磁盘上进行排序

          • MySQL无法利用索引完成的排序操作称为“文件排序”

        • Using temporary

          • 表示MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询group by

    • 修改SQL或者尽量让SQL走索引

      • MySQL的查询优化器有时候并非最优结果,可以使用force index语法指定合适的索引

  • 联合索引的最左匹配原则的成因

    • 联合索引中,如A_B组成联合索引,where A='' and B='' 走索引,where A='' 也走索引,where B='' 则不会走索引

    • MySQL会一直向右匹配知道遇到范围查询(>,<,between,like)就停止匹配

      • 如,a = 3 and b = 4 and c > 5 and d = 6

        • 如果建立(a,b,c,d)顺序的索引,d是用不到索引的

        • 如果建立(a,b,d,c)的索引,则都可以用到,a,b,d的顺序可以任意调整

    • 联合索引中,=和in可以乱序

      • 如,a=1 and b =2 and c = 3 建立(a,b,c)联合索引可以任意顺序,MySQL的查询优化器会优化成索引可以识别的形式 

    •   如下图,建立索引(col3,col2),MySQL会根据第一个字段,建立一个B+-Tree,因此使用此索引,必须要遵循最左匹配原则

  • 索引是建立的越多越好吗

    • 不是

    • 数据量小的表不需要建立索引,建立索引会增加额外的索引开销

    • 数据变更需要维护索引,因此更多的索引意味着更多的维护成本

    • 更多的索引意味着需要更多的空间

  • MyISAM与InnoDB关于锁方面的区别是什么

    • 读锁与写锁

      • 读锁

        • 读锁未释放时,无法再加上写锁

        • 读锁又称共享锁,一个表可以同时加上多个读锁

        • lock tables 表名 read; -> 手动给表加上读锁,unlock tables; -> 释放锁

        • 在select后面加上lock in share mode也可以加上共享锁

      • 写锁

        • 写锁未释放时,无法再加上读锁

        • 写锁又称排它锁,不能同时拥有多个

        • 在select后面加上for update关键字,可以加上排它锁

    • MyISAM

      • 默认用的是表级锁,不支持行级锁

        • 如,查询ID在1和2000000之间的数据时,对ID等于2000001的数据进行更新,更新会等待查询完成

      • 不支持事务

      • 当Select时,MyISAM引擎会为查询的表加上表级别的读锁

      • 当Insert,Update,Delete时,MyISAM引擎会为增删改的表加上表级别的写锁

      • 适合的场景

        • 频繁执行全表count语句,MyISAM用一个变量保存了表的行数,count时取出即可

        • 对数据进行增删改的频率不高,查询非常频繁时

        • 没有事务

    • InnoDB

      • 默认用的是行级锁,也支持表级锁

        • SQL语句用到索引才会加行级锁,否则加表级锁

      • 支持事务

        • MySQL默认事务自动提交

          • show variables like 'autocommit';

          • set autocommit = 0; -- 关闭自动提交,只针对当前session

          • set autocommit = 1; -- 开启自动提交,只针对当前session

          • 或使用begin transaction,手动控制事务

      • 当操作一行数据时,行级锁不会影响另一行数据的操作

      • 适合的场景

        • 数据增删改查都相当频繁

        • 可靠性要求比较高,要求支持事务

  • 数据库锁的分类

    • 按锁的力度划分

      • 表级锁

      • 行级锁

      • 页级锁

    • 按锁级别划分

      • 共享锁

      • 排它锁

    • 按枷锁方式划分

      • 自动锁

      • 显式锁

    • 按操作划分

      • DML锁

      • DDL锁

    • 按使用方式划分

      • 乐观锁

      • 悲观锁

  • 数据库事务的四大特性

    • ACID

    • 原子性(Atomic)

      • 事务中的操作,要么全做,要么全不做

    • 一致性(Consistency)

      • 事务应确保数据的状态从一个一致的状态,转变到另一个一致的状态

      • A和B共有两千元,A向B转账,不管怎么转,最终A和B还是应该有两千元

    • 隔离性(Isolation)

      • 多个事务并发执行时,一个事务的执行不应该影响另一个事务的执行

    • 持久性(Durability)

      • 一个事务一旦提交,它对数据的修改应该永久保存在数据库

  • 事务并发引起的问题及如何避免

    • 查看MySQL的事务隔离级别

      • select @@tx_isolation;

        • 查询当前会话的事务隔离级别,默认是REPEATABLE-READ,可重复读

      • set session transaction isolation level  read uncommitted;

        • 设置当前会话的事务隔离级别为未提交读

      • set session transaction isolation level  read committed;

        • 设置当前会话的事务隔离级别为提交读

      • set session transaction isolation level  repeatable read;

        • 设置当前会话的事务隔离级别为可重复读

      • set session transaction isolation level serializable;

        • 设置当前会话的事务隔离级别为串行化

    • 更新丢失,一个事务的更新覆盖了另一个事务的更新

    • 脏读,一个事务读到另一个事务未提交的数据

    • 不可重复读,一个事务中,对同一个数据的多次读取值不一致(别的事务对数据进行了更新)

    • 幻读,事务开启过程中,受到其他事务对新增或删除行的影响,像出现幻觉一样

  • InnoDB可重复读事务隔离级别下如何避免幻读

    • 表象

      • 快照读(非阻塞读),伪MVCC

    • 内在

      • next-key锁(行锁+Gap锁)

        • 行锁

        • Gap锁

          • Gap->索引中的间隙,锁一个范围

          • Gap锁 会用在非唯一索引或者不走索引的当前读中

            • 非唯一索引

              • 如下图,对所操作的记录周围的Gap上锁,即 (6,9],(9,11] 

              • 主键的值也会对范围产生影响,如插入('bb',6)成功,插入('dd',6)则会判定在Gap锁范围内

            • 不走索引

              • 锁住所有Gap,需要避免这种情况

          • 如果where条件全部命中,则不会用Gap锁,只会用记录锁

          • 如果where条件部分命中或者全不命中,则会加Gap锁

  • 快照读和当前读

    • 当前读

      • 读取的是数据的最新版本,并且读取后需要保证其他事务不能修改当前记录

      • 加了锁的增删改查语句,成为当前读

        • select ... lock in share mode

        • select ... for update

        • update

        • delete

        • insert

    • 快照读

      • 事务非Serializable级别下,不加锁的select操作,Serializable事务隔离级别时,由于是串行化,快照读会退化为当前读

      • 可重复读事务隔离级别下,快照读可能读到数据的历史版本

        • 事务A先进行快照读,事务B进行更新并提交,事务A的快照读可读到历史版本

        • 事务A开启,事务B进行更新并提交,事务A的快照读,读到的是最新版本

  • RC、RR级别下的InnoDB的非阻塞读(快照读)如何实现

    • 数据行里的DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID三个字段

      • DB_TRX_ID

        • 最近一次对本行做修改的事务ID

      • DB_ROLL_PTR

        • 回滚指针

      • DB_ROW_ID

        • 行ID

    • undo日志

    • read view

      • 做可见性判断

      • RR级别下,第一条快照读,会创建一个read view,将当前数据库的事务状态记录,同一事务中再次进行快照读时,使用的是同一个read view

      • RC级别下,每次进行快照读,都会创建一个新的read view

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值