一、数据库架构
1. 考点思维导图
2. 如何设计一个关系型数据库(RDBMS)
存储(文件系统)
数据库最主要的功能就是存储我们的数据,因此他会有一个存储模块来负责存储我们的数据,存储模块就相当于我们的OS文件系统,将数据最终持久化存入磁盘中,如机械硬盘或者SSD固态硬盘中亦或者他们的磁盘阵列矩阵中。
程序实例
我还还需要组织并且用到这些数据,因此需要有程序实例,用逻辑结构来映射出物理结构来,并且在程序中提供获取以及管理数据的方式还有必要的问题追踪机制。接下来细分一下程序的模块,首先我们需要对数据的格式以及文件的风格进行统一的管理,即把物理数据通过逻辑的形式给组织和表示出来,于是便涉及到咱们程序的存储管理模块。作为一款很追求性能的软件,我们没有好好利用内存,因此为了更快更好的优化我们的程序,我们应该想到做项目的时候的一种普遍做法,便是引入缓存机制,把取出来的数据块放进缓存里,下次需要的时候直接从内存返回,而不用发生io,这里需要补充的是,刚才咱们说到的一次性加载多个模块或者页,这些块里包含数据行有相当的一部分并不是本次查询我们需要的行,但是根据一旦某行数据被访问,那么它周围的数据也极有可能被返回的经验,咱们缓存的非本次数据也能起到优化访问效率的作用,一次加载的这个块中它有abc三条数据,我们只需要a的数据,b和c也会被加载到内存中,按照经验来说,下次b和c被访问到的几率是非常大的,也就是他也能提升我们的访问性能。关于如何管理缓存有很多的方法,如LRU等,也不能说那个方法最好,某种方法适用于某个具体的场景。我们还要能提供外界指令来操作我们的数据库,即可读的sql语言,那我们就需要一个sql的解析模块,将sql编译解析转换成机器可识别的指令,为了进一步提高sql执行效率,还是将sql缓存入缓存里,编译好的sql方便下次来了直接进行解析就可以了,注意缓存不宜过大,算法中应该有淘汰机制,淘汰掉一些之后不常用的数据。此外,咱们做的sql操作需要记录下来方便我们做数据库的主从同步或者灾难恢复,因此我们就需要有日志管理来记录操作。我们还需要提供给用户管理数据的私密空间,即权限划分。设计系统除了考虑正常的功能还要考虑异常的情况,怎么处理异常的情况呢,我们就需要引入容灾机制。为了进一步提升查询的速度以及让数据库支持并发,咱们还需要引入最能够突出数据库特点的两个模块,索引管理和锁模块。
答案
如何设计一个关系型数据库,首先咱们要换分成两个模块。
- 一个是存储部分,该部分类似一个文件系统,来将数据持久化到存储设备当中。
- 光有存储是不行的,还需要程序实例模块,来对存储进行逻辑上的管理。而程序实例部分将包含数据的逻辑关系转换成物理存储关系的存储管理模块;优化执行效率的缓存模块;将sql语句进行解析的sql解析模块;记录操作的日志管理模块;进行多用户管理的权限划分模块;灾难恢复模块;优化数据查询效率的索引模块以及使得数据库支持并发操作的锁模块。
3. 为什么要使用索引
- 快速查询数据
最简单的查询方式就是全表扫描,即将整张表的数据全部或者分批次加载到内存当中,最小的存储单位是块或者页,他们是由多行数据来组成的,然后我们将这些块都加载进来,然后逐个块或者逐个页去轮询,找到我们要的目标并返回,这种方式普遍认为是非常慢的(但在数据量非常少的情况下是快的),在数据量很大的表里该方法显然不适用了。
很多情况下我们都要避免全表扫描的发生,所以咱们的数据库得引入一种更高效的机制,这便是索引了,它的灵感来源于字典,在字典里我们只需要把一些关键信息组织起来比如说偏旁部首这些,查询的时候依据这些信息的指引就能够查到我们的页面很快就能查到我们要查的字了,而这些关键信息以及查找数据的方式便组成了我们的索引,通过索引来大幅提升查询速度。
4. 什么样的信息能成为索引
- 主键、唯一键以及普通键
把该记录限定在一定查找范围内的字段,就是我们刚才所说的关键信息,主键便是一个很好的索引切入点,当然其他键位包块唯一键普通键等也可以作为索引。
5. 索引的数据结构
- 生成索引,建立二叉查找树进行二分查找。变种平衡二叉树和红黑树。
- 生成索引,建立B-Tree结构进行查找。
- 生成索引,建立B+-Tree结构进行查找。这是主要使用的数据结构。
- 生成索引,建立Hash结构进行查找。
6. 密集索引和稀疏索引的区别
二、优化你的索引-运用二叉查找树
三、优化你的索引-运用B数
1. 定义
- 根节点至少包括两个孩子;
- 树中每个节点最多含有m个孩子(m>=2));
- 除根节点外,其他每个节点至少有ceil(m/2)个孩子;
- 所有叶子节点都位于同一层;
- 假设每个非终端节点中包含有n个关键字信息,其中
- Ki(i=i...n)为关键字,且关键字按顺序升序排序K(i-1)<Ki
- 关键字的个数n必须满足:[ceil(m/2)-1]<=n<=m-1
- 非叶子节点的指针:P[1],P[2],...,P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其他P[i]指向关键字属于(K[i-1],K[i])的子树。
四、优化你的索引-运用B+树
1. 定义
B+树是B树的变体,其定义基本与B树相同,除了:
- 非叶子节点的子树指针与关键字个数相同;
- 非叶子节点的子树指针P[i],指向关键字值[K[i], K[i+1])的子树;
- 非叶子节点仅用来索引,数据都保存在叶子节点中;
- 所有叶子节点均有一个链指针指向下一个叶子节点。
2. 结论
B+Tree更适合用来做存储索引
- B+树的磁盘读写代价更低;
B+树的内部结构并没有指向关键字具体信息的指针,也就是不存放数据,只存放索引信息,因此其内部节点相对于B树更小,如果把所有同一内部节点的关键字存在同一盘块中,这个盘块所容纳的关键字数量也越多,一次性读入内存中的所要查找的关键字也就越多,相对来说IO读写次数也就降低了。 - B+树的查询效率更加稳定;
由于内部节点并不是指向文件内容的节点,而只是叶子节点中关键字的索引,所以任何关键字的查找必须走一条从根节点到叶子节点的路,所有关键字查询的长度相同,导致每一个关键字查询的效率几乎是相同的,稳定的O(lgn)。 - B+树更有利于对数据库的扫描。
B树在提高IO性能的同时并没有解决元素遍历效率低下的问题,而B+树只需要遍历叶子节点就可以解决对全部关键字信息的扫描,所以对于数据库中频繁使用的范围查询是非常方便的,具有更高的性能。
五、优化你的索引-运用Hash以及BitMap
1. Hash特点
经过哈希函数的运算,只需经过一次定位,便能找到查询数据所在的头,不像B+索引需要从根节点到非叶子结点再到叶子节点最后才能访问到我们需要的数据,这样可能会经过多次的IO访问,所以哈希索引的查询效率理论上要高于B+树索引。
2. Hash缺点
- 仅仅能满足“=”,“IN”,不能使用范围查询;
由于哈希索引比较的是进行哈希运算之后的查询值,所以他只能用于等值的过滤,不能用于基于范围的查询,因为经过相应的哈希算法处理之后的哈希值的大小关系,并不能保证和哈希运算前完全一样。 - 无法被用来避免数据排序操作;
由于哈希索引是经过哈希运算之后的值,而且哈希值的大小关系并不一定和哈希运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何的排序运算。 - 不能利用部分索引键查询;
对于组合索引,哈希索引在计算哈希值的时候是组合键,就是将组合索引键合并之后再一起进行运算的哈希的值,而不是单独计算哈希的值,所以经过组合索引的前面一个或几个索引键进行查询的时候,哈希索引也无法被利用,而B+树支持利用组合索引中的部分索引的。 - 不能避免表扫描
哈希索引是将索引键通过哈希运算之后将运算结果的哈希值所对应的行指针信息存放在一个bucket当中,由于不同索引键存在相同的哈希值,所以即使取出满足某个哈希键值的那些数据了,也无法从哈希索引中直接完成查询,还是要通过访问bucket中的实际数据进行相应的比较,所以这个是不能避免表扫描的原因。 - 遇到大量哈希值相等的情况后性能并不一定就会比B-Tree索引高;
对于选择性比较低的索引键如果创建哈希索引那么将会存在大量记录指针信息存放于一个bucket的情况,从而造成整体性能非常低下。
3. BitMap索引是个神器(位图索引)
当表中的某个字段只有几种值的时候,比如要表示性别的时候,只有男女两种情况,如果仅仅是为了在这个字段上进行高效的统计此时呢用位图索引是一个最佳的选择,不过大家要注意的是目前很少的数据库支持位图索引,已知比较主流的是Oracle数据库。
4. BitMap结构
位图索引的结构类似于B+树。B+树用来定位叶子节点,这些节点包含指定键值的位图段,位图段就是位于叶子节点上,它的段信息就在这里,由于数据的值种类固定,所以在存储方式上会先按照状态值进行分开,然后每一种值的空间会存储每个实际的数据行是否是这个值,在这种情况下,由于只需要存储是与否,所以通常只需要一个bit位来存放,因此理论上一个叶子块可以存放非常多的bit位来表示不同的行,因此用它来统计时是非常快的,下载到内存中后,几乎是纯CPU的叠加操作。
5. BitMap缺点
- 只适用于某个字段的值只有固定的几个的这种情况;
- 它的锁的力度非常的大,当尝试新增或者修改一条数据的时候,通常与他在同一个位图的数据操作都会被锁住,因为某行所在的位置顺序有可能会因为数据的添加或者删除而发生改变,因此在发生改变的时候必须得将其锁定以防止取错数据,所以它不适合高并发的联机处理系统,即咱们常见的OLTP系统,而适合于并发较少而统计运算较多的OLAP类系统。
6. 总结
通常索引的结构是B+树,比较小众的也有哈希结构还有BitMap等。
六、密集索引和稀疏索引的区别
1. 区别
- 密集索引文件中的每一个搜索码值都对应一个索引值。
叶子节点保存的不仅仅是键值,还保存了位于同一行记录里的其他列的信息,由于密集索引决定了表的物理排列顺序,一个表只能有一个物理排列顺序,所以一个表只能创建一个密集索引。 -
稀疏索引文件只为索引码的某些值建立索引项。
叶子节点仅保存了键位信息以及该行数据的地址,有的稀疏索引仅保存了键位信息以及主键,定位到叶子节点之后,仍然需要通过地址或者主键信息进一步定位到数据。
2. MySQL
InnoDB
- 若一个主键被定义,该主键则作为密集索引;
- 若没有主键被定义,该表的第一个唯一非空索引则作为密集索引;
- 若不满足以上条件,innodb内部会生成一个隐藏主键(密集索引);
这个主键是一个6字节的列,该列的值会随着数据的插入而自增。
也就是说InnoDB必须有一个主键,该主键必须作为唯一的密集索引而存在。 - 非主键索引存储相关键位和其对应的主键值,包含二次查找;
InnoDB使用的是密集索引,将主键组织到一颗B+树中,而行数据就存储在叶子节点上,因为InnoDB的主键索引和对应的数据是保存在同一个文件当中的,索引检索的时候在加载叶子节点的主键进入内存的同时也加载了对应的数据,即用where id = 14这样的条件查询主键则按照B+树的检索算法即可查找到对应的叶子节点,并获得对应的行数据。
若对稀疏索引进行条件筛选,则需要经历两个步骤,第一步在稀疏索引的B+树中检索该键,定位到主键信息,获取到主键信息之后,还需要经历第二步,第二步就是使用这个主键where id=14在B+树中在执行一遍我们B+树的检索操作,最终在到达叶子节点获取整行的数据。
而MyISAM使用的均是稀疏索引,稀疏索引的两颗B+树看上去没有什么不同,节点的结构完全一致,只是存储的内容不一样了而已,主键索引B+树存储了主键,辅助键索引B+树存储了辅助键,表数据存储在独立的地方,就是索引和数据是分开存储的,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别,由于索引数是独立的,通过辅助键检索无需访问主键的索引数。
七、索引额外的问题之如何调优Sql
1. 为什么要使用索引
因为索引能够让我们避免全表扫描去查找数据,提升检索效率
2. 什么样的信息能成为索引
主键,唯一键等,只要是能让数据具备一定区分性的字段,都能成为索引
3. 索引的数据结构
主流是B+树,还有Hash结构和BitMap等,其中MySQL数据库不支持BitMap索引,同时,基于InnoDB以及MyISAM引擎的MySQL不显式支持Hash
4. 密集索引和稀疏索引的区别
参上
5. 如何定位并优化慢查询Sql
具体场景具体分析
根据慢日志定位慢查询sql
使用explain等工具分析sql
修改sql或者尽量让sql走索引