一个面试题引发的【血案】
- 如何设计一个关系型数据库?
设计
存储(文件系统)
- 类似文件系统,将数据持久化到存储设备当中。
程序实例
- 存储管理:将数据的逻辑关系转换成物理存储管理
- 缓存机制:优化执行效率
- SQL解析 (将sql缓存起来):将sql语句执行解析
- 日志管理:操作日志处理
- 权限划分
- 容灾机制(异常机制):灾难恢复
- 索引管理:优化数据查询效率
- 锁管理:支持并发操作
索引
常见问题
- 为什么要使用索引
- 什么样的信息能成为索引
- 索引的数据结构
- 密集索引和稀疏索引的区别
1. 为什么要使用索引?
- 快速查询数据
- 避免全表扫描查找数据
2. 什么样的信息能成为索引
- 主键、唯一键和普通键等能让数据具备一定区分性的字段
3. 索引的数据结构
- 生成索引,建立二叉查找树进行二分查找
- 生成索引,建立B-Tree结构进行查找
- 生成索引,建立B+-Tree结构进行查找
- 生成索引,建立Hash结构进行查找
4.优化索引
二叉查找树
二叉查找树(平衡二叉树-左子树和右子树高度差不超过1),查询采用二分查找,查询的复杂度为O(logn)。
缺点:
- 容易变成线性二叉树,此时时间复杂度为 O(n),可以通过树的旋转保证这棵树是平衡二叉树;
- IO是影响程序运行速度的瓶颈,数据查询过程检索深度每增加1就发生一次IO
B-Tree
B树-平衡多路查找树,每个节点最多可以有m个孩子那么这样的树就是m阶B树,每个存储块中包含关键字,指向孩子的指针,m是存储块的容量和数据库配置决定一般会很大。
- 根节点至少包括两个孩子
- 树中每个节点最多含有m个孩子(m>=2)
- 除根节点和叶节点外,其他每个节点至少有ceil(m/2)个孩子 ceil(向上取整)取上限
- 所有叶子节点都位于同一层,叶子节点的高度都一样;目的是让每个索引块尽可能存储更多的信息,控制树的高度尽可能减少IO的次数
- 假设每个非终端节点中包含n个关键字信息,其中:关键字升序排列;关键字的个数比子树的个数小1;同位置子树的值小于同位置关键字的值即 Ki-1<Pi<Ki (P子树值,K关键字值)
B+-Tree
B+树是B树的变体,定义基本相同,除了:
- 非叶子节点的子树指针个数与关键字个数相同
- 非叶子节点的子树指针P[i],指向关键字值[K[i],K[i+1]]的子树
- 非叶子节点仅用来索引,数据都保存在叶子节点中
- 所有叶子节点均有一个链指针指向下一个叶子节点,按大小顺序链接(范围统计,大于10的查询可以直接叶子节点横向统计)
B+树更适合做存储索引
- B+树的磁盘读写代价更低(存储块中的关键字越多,树的高度越小,一次读入内存的关键字越多,减少了磁盘的IO)
- 查询效率更加稳定(O(logn),每次查询的深度一样)
- 更有利于对数据库的扫描(只用对叶子节点进行扫描)
Hash索引
缺点:
- 仅仅能满足“=”,“IN”,不能使用范围查询
- 无法被用来避免数据的排序操作
- 不能利用部分索引键查询
- 不能避免表扫描
- 遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高
5. 密集索引和稀疏索引的区别
- 密集索引文件中的每个搜索码值都对应一个索引值
- 稀疏索引文件只为索引码的某些值建立索引项
InnoDB
- 若一个主键被定义,该主键则作为密集索引
- 若没有主键被定义,该表的第一个唯一非空索引则作为密集索引
- 若不满足以上条件,innoDB内部会生成一个隐藏索引(密集索引)
- 非主键索引存储相关键位和其对应的主键值,包含两次查找
MyISAM
MyISAM均为稀疏索引
6. 如何定位并优化慢查询Sql
- 根据慢日志定位慢查询sql
- 打开slow_query_log
- slow_query_log_file慢日志查询sql存储位置
- long_query_time当sql执行时间超过这个值就会将sql记录到slow_query_log_file对应的慢日志;
- 执行
set global slow_query_log = on;打开慢查询日志 - 执行
set global slow_query_time = 1;设置慢查询时间
- 使用explain工具分析sql
explain select name from sys_user order by name desc;分析sql语句,如果结果中type是index/all则sql是全表扫描(这时sql需要优化),另一个结果字段extra如下图: - 修改sql或者尽量让sql走索引提升效率
添加索引:
alter table sys_user add index idx_name(name);
7. 联合索引的最左匹配原则的成因
8. 索引是建立的越多越好吗
- 数据量小的表不需要建立索引,建立会增加额外的索引开销
- 维护成本提高
- 更多的索引意味着需要更多的空间
锁模块
1.MyISAM与InnoDB关于锁方面的区别是什么
- MyISAM默认用的是表级锁,不支持行级锁;
- InnoDB默认用的是行级锁,也支持表级锁
表级锁
MyISAM存储引擎在进行select操作是,会自动对表加上读锁;对表进行增删改的时候,会对表增加一个写锁。当加上读锁时,另一个session对表进行写操作时会阻塞,直到读锁释放。
lock tables sys_user read | write添加读/写锁unlock tables释放所有锁- 上了读锁(共享锁),支持继续再上读锁,不支持上写锁
- 当上了写锁,就不能上读|写锁,需等写锁释放;写锁又称排它锁
InnoDB用的二段锁-分加锁和解锁两个步骤(解锁即commit)
show variables like 'autocommit';查看是否自动提交事务set autocommit = 0;关闭自动提交- innoDb存储引擎对select语句进行了改进,该select并未对当前行上锁(非阻塞select)
- 在sql没有用索引的时候,采用表级锁
MyISAM适合的场景
- 频繁执行全表count语句
- 对数据进行增删改的频率不高,查询非常频繁
- 没有事务的场景
InnoDB适合的场景
- 数据增删改查都相当频繁
- 可靠性要求比较高,需要支持事务
数据库锁的分类
- 按锁的粒度划分:表级锁、行级锁、业级锁(不常用的BDB引擎)
- 按锁级别划分:共享锁、排它锁
- 按加锁方式划分:自动锁、显示锁
- 按操作划分:DML锁(对数据进行操作上的锁)、DDL锁(对表结构变更加上的锁)
- 按使用方式划分:乐观锁、悲观锁
2.数据库事务的四大特性
ACID
- 原子性(Atomic):事务包含的所有操作要么全部执行要么全部失败回滚
- 一致性(Consistency):事务应确保数据库的状态,从一个一致状态到另一个一致状态
- 隔离性(Isolation):事务与事务之间互不影响
- 持久性(Durability):事务提交后对数据库的修改永久保存到数据库中(redo_log_file文件保存事务对数据库的操作)
3.事务隔离级别以及各级别下的并发访问问题
事务并发访问引起的问题以及如何避免
- 更新丢失(一个事务的更新覆盖了另一个事务的更新)--mysql所有事务隔离级别(最低READ-UNCOMMITTED)在数据库层面上均可避免
- 脏读--READ-COMMITTED事务隔离级别以上可避免 可通过
select @@tx_isolation;查看事务隔离级别,设置事务隔离级别:set session transaction isolation level read uncommitted; - 不可重复读--REPEATABLE-READ事务隔离级别以上可避免
- 幻读--SERIALIZABLE事务隔离级别以上可避免
- 事务隔离级别在SERIALIZABLE级别,所有的sql都会加上锁
- 出于性能考虑,事务隔离级别越高,安全性越高,串行化执行越严重,降低数据库的并发度
- ORACLE默认为READ-COMMITTED
- MYSQL默认为REPEATABLE-READ
4.InnoDB可重复读隔离级别下如何避免幻读
- 表象:快照读(非阻塞读)--伪MVCC
- 内在:next-key锁(行锁+gap锁)
5.RC、RR级别下的InnoDB的非阻塞读如何实现
- 数据行里的DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID字段
- undo日志
- read view
关键语法
- GROUP BY
- 满足"SELECT子句中的列名必须为分组列或列函数"
- 列函数对于group by子句定义的每个组各返回一个结果
- HAVING
- 通常与GROUP BY子句一起使用
- WHERE过滤行,HAVING过滤组
- 出现在同一sql的顺序:WHERE>GROUP BY>HAVING
- 省略GROUP BY子句,HAVING和WHERE一样
- 查询平均成绩大于60的语句
select student_id,avg(score) from score group by student_id having avg(score)>60
- 统计相关:COUNT, SUM, MAX, MIN, AVG