本文共 6991 字,大约阅读时间需要 23 分钟。
可以看到上图将数据库分为四个模块,分别是核心组件,查询管理器,数据管理器和工具类
核心组件
安全管理器
用于对用户的验证和授权。客户端管理器
用于管理客户端连接内存管理器
为了避免磁盘I/O带来的性能损失,需要大量的内存。但是如果你要处理大容量内存你需要高效的内存管理器,尤其是你有很多查询同时使用内存的时候。文件系统管理器
磁盘I/O是数据库的首要瓶颈。具备一个文件系统管理器来完美地处理OS文件系统甚至取代OS文件系统,是非常重要的。网络管理器
网路I/O是个大问题,尤其是对于分布式数据库。所以一些数据库具备自己的网络管理器。进程管理器
很多数据库具备一个需要妥善管理的进程/线程池。再者,为了实现纳秒级操作,一些现代数据库使用自己的线程而不是操作系统线程。工具
查询管理器
查询解析器
检查查询是否合法查询重写器
预优化查询查询优化器
优化查询查询执行器
编译和执行查询数据管理器
用于处理事务
缓存管理器
数据被使用之前置于内存,或者数据写入磁盘之前置于内存数据访问管理器
访问磁盘中的数据客户端管理器是处理客户端通信的。客户端可以是一个(网站)服务器或者一个最终用户或最终应用。客户端管理器通过一系列知名的API(JDBC, ODBC, OLE-DB …)提供不同的方式来访问数据库。
客户端管理器也提供专有的数据库访问API。
当连接到数据库时:
管理器首先检查你的验证信息(用户名和密码),然后检查你是否有访问数据库的授权。这些权限由DBA分配。
然后,管理器检查是否有空闲进程(或线程)来处理你对查询。
管理器还会检查数据库是否负载很重。
管理器可能会等待一会儿来获取需要的资源。如果等待时间达到超时时间,它会关闭连接并给出一个可读的错误信息。
然后管理器会把你的查询送给查询管理器来处理。
因为查询处理进程不是『不全则无』的,一旦它从查询管理器得到数据,它会把部分结果保存到一个缓冲区并且开始给你发送。
如果遇到问题,管理器关闭连接,向你发送可读的解释信息,然后释放资源。
操作过程:
功能:
1. 检查语法和关键字顺序SELECT 写成 SLECTWHERE 写在 SELECT 之前
2. 检查表和字段
1.表是否存在2.表的字段是否存在3.对某类型字段的 运算 是否 可能(比如,你不能将整数和字符串进行比较,你不能对一个整数使用 substring() 函数)
3. 检查在查询中是否有权限来读写数据
在解析过程中SQL 查询被转换为内部表示(通常是一个树) 如果一些解析正常 内部会把结果送到查询重写器
目标:
基本操作
视图合并
如果你在查询中使用视图,视图就会转换为它的 SQL 代码子查询扁平化
子查询是很难优化的,因此重写器会尝试移除子查询SELECT PERSON.*FROM PERSONWHERE PERSON.person_key IN(SELECT MAILS.person_keyFROM MAILSWHERE MAILS.mail LIKE 'christophe%');
会被转换成
SELECT PERSON.*FROM PERSON, MAILSWHERE PERSON.person_key = MAILS.person_keyand MAILS.mail LIKE 'christophe%';
3.去除不必要的运算符
比如,如果你用了 DISTINCT,而其实你有 UNIQUE 约束(这本身就防止了数据出现重复),那么 DISTINCT 关键字就被去掉了4.排除冗余的联接
如果相同的 JOIN 条件出现两次,比如隐藏在视图中的 JOIN 条件,或者由于传递性产生的无用 JOIN,都会被消除。5.常数计算赋值
如果你的查询需要计算,那么在重写过程中计算会执行一次。比如 WHERE AGE > 10+2 会转换为 WHERE AGE > 12 , TODATE(“日期字符串”) 会转换为 datetime 格式的日期值6.(高级)分区裁剪(Partition Pruning)
如果你用了分区表,重写器能够找到需要使用的分区。7.(高级)物化视图重写(Materialized view rewrite)
如果你有个物化视图匹配查询谓词的一个子集,重写器将检查视图是否最新并修改查询,令查询使用物化视图而不是原始表。8.(高级)自定义规则
如果你有自定义规则来修改查询(就像 Oracle policy),重写器就会执行这些规则。9.(高级)OLAP转换
分析/加窗 函数,星形联接,ROLLUP 函数……都会发生转换(但我不确定这是由重写器还是优化器来完成,因为两个进程联系很紧,必须看是什么数据库)。当你要求数据库收集统计信息,数据库会计算下列值
这些统计信息会帮助优化器估计查询所需的磁盘 I/O、CPU、和内存使用
在研究 B+树的时候我们谈到了索引,要记住一点,索引都是已经排了序的。
仅供参考:还有其他类型的索引,比如位图索引,在 CPU、磁盘I/O、和内存方面与B+树索引的成本并不相同。
另外,很多现代数据库为了改善执行计划的成本,可以仅为当前查询动态地生成临时索引。
在应用联接运算符(join operators)之前,你首先需要获得数据。以下就是获得数据的方法。
A join B:称A是外联系,B是内联系,N是外联系中元素个数,M是内联系中元素个数
原理如下
1. 针对外关系的每一行 2. 查看内关系里的所有行来寻找匹配的行原理:
1) 读取内关系的所有元素 2) 在内存里建一个哈希表 3) 逐条读取外关系的所有元素 4) (用哈希表的哈希函数)计算每个元素的哈希值,来查找内关系里相关的哈希桶内 5) 是否与外关系的元素匹配。原理
1.(可选)排序联接运算:两个输入源都按照联接关键字排序。[归并排序] 2.合并联接运算:排序后的输入源合并到一起。三种连接算法的比较
在这个阶段,我们有了一个优化的执行计划,再编译为可执行代码。然后,如果有足够资源(内存,CPU),查询执行器就会执行它。计划中的操作符 (JOIN, SORT BY …) 可以顺序或并行执行,这取决于执行器。为了获得和写入数据,查询执行器与数据管理器交互,本文下一部分来讨论数据管理器。
查询管理器执行了查询,需要从表和索引获取数据,于是向数据管理器提出请求
但是有 2 个问题:
查询执行器不会直接从文件系统拿数据,而是向缓存管理器要。缓存管理器有一个内存缓存区,叫做缓冲池,从内存读取数据显著地提升数据库性能。
缓存管理器需要在查询执行器使用数据之前得到数据,否则查询管理器不得不等待数据从缓慢的磁盘中读出来。
有时查询执行器不知道它需要什么数据,有的数据库也不提供这个功能。相反,它们使用一种推测预读法(比如:如果查询执行器想要数据1、3、5,它不久后很可能会要 7、9、11),或者顺序预读法(这时候缓存管理器只是读取一批数据后简单地从磁盘加载下一批连续数据)。
LRU 最近最少使用(Least Recently Used)算法
之前探讨的都是读缓存 —— 在使用之前预先加载数据。用来保存数据、成批刷入磁盘,而不是逐条写入数据从而造成很多单次磁盘访问
要记住,缓冲区保存的是页(最小的数据单位)而不是行(逻辑上/人类习惯的观察数据的方式)。缓冲池内的页如果被修改了但还没有写入磁盘,就是脏页。有很多算法来决定写入脏页的最佳时机,但这个问题与事务的概念高度关联,下面我们就谈谈事务。
原子性(Atomicity): 事务『要么全部完成,要么全部取消』,即使它持续运行10个小时。如果事务崩溃,状态回到事务之前(事务回滚)。
隔离性(Isolation): 如果2个事务 A 和 B 同时运行,事务 A 和 B 最终的结果是相同的,不管 A 是结束于 B 之前/之后/运行期间。
持久性(Durability): 一旦事务提交(也就是成功执行),不管发生什么(崩溃或者出错),数据要保存在数据库中。
一致性(Consistency): 只有合法的数据(依照关系约束和函数约束)能写入数据库,一致性与原子性和隔离性有关。
事务的隔离级别并不是越高越好,越高的事务隔离级别会增加锁的数量和事务等待锁的时间
多个事务在相同时刻对相同数据进行读写操作
理想的方法是悲观锁
原理是:
这个锁叫排他锁。
但是对一个仅仅读取数据的事务使用排他锁非常昂贵,因为这会迫使其它只需要读取相同数据的事务等待。因此就有了另一种锁,共享锁。
共享锁
原理:
锁管理器是添加和释放锁的进程,在内部用一个哈希表保存锁信息(关键字是被锁的数据),并且了解每一块数据是:
死锁
2个事务永远在等待一块数据解决方法:
在死锁发生时,锁管理器要选择取消(回滚)一个事务,以便消除死锁。我们已经知道,为了提升性能,数据库把数据保存在内存缓冲区内。但如果当事务提交时服务器崩溃,崩溃时还在内存里的数据会丢失,这破坏了事务的持久性。
你可以把所有数据都写在磁盘上,但是如果服务器崩溃,最终数据可能只有部分写入磁盘,这破坏了事务的原子性。
事务作出的任何修改必须是或者撤销,或者完成。
有 2 个办法解决这个问题:
1) 每个对数据库的修改都产生一条日志记录,在数据写入磁盘之前日志记录必须写入事务日志。
2) 日志记录必须按顺序写入;记录 A 发生在记录 B 之前,则 A 必须写在 B 之前。
3) 当一个事务提交时,在事务成功之前,提交顺序必须写入到事务日志。
如果事务日志写得太慢,整体都会慢下来
目标
1) 写日志的同时保持良好性能 2) 快速和可靠的数据恢复先了解日志里保存的内容
事务的每一个操作(增/删/改)产生一条日志,由如下内容组成:
日志缓冲区
当查询执行器要求做一次修改:
1) 缓存管理器将修改存入自己的缓冲区;
2) 日志管理器将相关的日志存入自己的缓冲区; 3) 到了这一步,查询执行器认为操作完成了(因此可以请求做另一次修改); 4) 接着(不久以后)日志管理器把日志写入事务日志,什么时候写日志由某算法来决定。 5) 接着(不久以后)缓存管理器把修改写入磁盘,什么时候写盘由某算法来决定。ARIES从崩溃中恢复有三个阶段:
转载地址:http://ohqli.baihongyu.com/