前言
原文地址:http://blog.jobbole.com/100349/
国内大佬翻译的文章,因为文章较长,不适合碎片化阅读,因此分为几篇文章来转载,满满的干货,外链在微信上不能显示
我们已经了解了数据库内部的基本组件,现在我们需要回来看看数据库的全貌了。
数据库是一个易于访问和修改的信息集合。不过简单的一堆文件也能达到这个效果。事实上,像SQLite这样最简单的数据库也只是一堆文件而已,但SQLite是精心设计的一堆文件,因为它允许你:
数据库一般可以用如下图形来理解:
撰写这部分之前,我读过很多书/论文,它们都以自己的方式描述数据库。所以,我不会特别关注如何组织数据库或者如何命名各种进程,因为我选择了自己的方式来描述这些概念以适应本文。区别就是不同的组件,总体思路为:数据库是由多种互相交互的组件构成的。
核心组件:
工具:
查询管理器:
数据管理器:
在本文剩余部分,我会集中探讨数据库如何通过如下进程管理SQL查询的:
客户端管理器是处理客户端通信的。客户端可以是一个(网站)服务器或者一个最终用户或最终应用。客户端管理器通过一系列知名的API(JDBC, ODBC, OLE-DB …)提供不同的方式来访问数据库。
客户端管理器也提供专有的数据库访问API。
当你连接到数据库时:
这部分是数据库的威力所在,在这部分里,一个写得糟糕的查询可以转换成一个快速执行的代码,代码执行的结果被送到客户端管理器。这个多步骤操作过程如下:
这里我不会过多探讨最后两步,因为它们不太重要。
每一条SQL语句都要送到解析器来检查语法,如果你的查询有错,解析器将拒绝该查询。比如,如果你写成”SLECT …” 而不是 “SELECT …”,那就没有下文了。
但这还不算完,解析器还会检查关键字是否使用正确的顺序,比如 WHERE 写在 SELECT 之前会被拒绝。
然后,解析器要分析查询中的表和字段,使用数据库元数据来检查
接着,解析器检查在查询中你是否有权限来读取(或写入)表。再强调一次:这些权限由DBA分配。
在解析过程中,SQL 查询被转换为内部表示(通常是一个树)。
如果一切正常,内部表示被送到查询重写器。
在这一步,我们已经有了查询的内部表示,重写器的目标是:
重写器按照一系列已知的规则对查询执行检测。如果查询匹配一种模式的规则,查询就会按照这条规则来重写。下面是(可选)规则的非详尽的列表:
例如:
SELECT PERSON.*
FROM PERSON
WHERE PERSON.person_key IN
(SELECT MAILS.person_key
FROM MAILS
WHERE MAILS.mail LIKE 'christophe%');
会转换为:
SELECT PERSON.*
FROM PERSON, MAILS
WHERE PERSON.person_key = MAILS.person_key
and MAILS.mail LIKE 'christophe%';
【译者注: 物化视图 。谓词,predicate,条件表达式的求值返回真或假的过程】
重写后的查询接着送到优化器,这时候好玩的就开始了。
研究数据库如何优化查询之前我们需要谈谈统计,因为没有统计的数据库是愚蠢的。除非你明确指示,数据库是不会分析自己的数据的。没有分析会导致数据库做出(非常)糟糕的假设。
但是,数据库需要什么类型的信息呢?
我必须(简要地)谈谈数据库和操作系统如何保存数据。两者使用的最小单位叫做页或块(默认 4 或 8 KB)。这就是说如果你仅需要 1KB,也会占用一个页。要是页的大小为 8KB,你就浪费了 7KB。
回来继续讲统计! 当你要求数据库收集统计信息,数据库会计算下列值:
这些统计信息会帮助优化器估计查询所需的磁盘 I/O、CPU、和内存使用
对每个列的统计非常重要。 比如,如果一个表 PERSON 需要联接 2 个列: LAST_NAME, FIRST_NAME。 根据统计信息,数据库知道FIRST_NAME只有 1,000 个不同的值,LAST_NAME 有 1,000,000 个不同的值。 因此,数据库就会按照 LAST_NAME, FIRST_NAME 联接。 因为 LAST_NAME 不大可能重复,多数情况下比较 LAST_NAME 的头 2 、 3 个字符就够了,这将大大减少比较的次数。
不过,这些只是基本的统计。你可以让数据库做一种高级统计,叫直方图。直方图是列值分布情况的统计信息。例如:
这些额外的统计会帮助数据库找到更佳的查询计划,尤其是对于等式谓词(例如: WHERE AGE = 18 )或范围谓词(例如: WHERE AGE > 10 and AGE < 40),因为数据库可以更好的了解这些谓词相关的数字类型数据行(注:这个概念的技术名称叫选择率)。
统计信息保存在数据库元数据内,例如(非分区)表的统计信息位置:
统计信息必须及时更新。如果一个表有 1,000,000 行而数据库认为它只有 500 行,没有比这更糟糕的了。统计唯一的不利之处是需要时间来计算,这就是为什么数据库大多默认情况下不会自动计算统计信息。数据达到百万级时统计会变得困难,这时候,你可以选择仅做基本统计或者在一个数据库样本上执行统计。
举个例子,我参与的一个项目需要处理每表上亿条数据的库,我选择只统计10%,结果造成了巨大的时间消耗。本例证明这是个糟糕的决定,因为有时候 Oracle 10G 从特定表的特定列中选出的 10% 跟全部 100% 有很大不同(对于拥有一亿行数据的表,这种情况极少发生)。这次错误的统计导致了一个本应 30 秒完成的查询最后执行了 8 个小时,查找这个现象根源的过程简直是个噩梦。这个例子显示了统计的重要性。
注:当然了,每个数据库还有其特定的更高级的统计。如果你想了解更多信息,读读数据库的文档。话虽然这么说,我已经尽力理解统计是如何使用的了,而且我找到的最好的官方文档来自PostgreSQL。