良好的schema设计原则是普遍适用的,但 MySQL 有它自己的实现细节要注意。概括来说,尽可能保持任何东西小而简答总是最好的。MySQL 更喜欢简单,需要使用数据库的人也应该会同样喜欢简单的原则:
- 尽量避免过度的设计,例如会导致及其复杂查询的schema设计,或者有很多列的表设计。
- 使用小而简单的合适数据类型,除非真实数据模型中有确切需要,否则应该尽可能避免使用NULLL值。
- 尽量使用相同的数据类型存储相同或相关的值,尤其是在关联条件中使用的列。
- 注意可变长字符串,其在临时表和排序时,可能导致悲观的按最大长度分配内存。
- 尽量使用整形定义表示列。
- 避免使用MySQL 已经遗弃的特性,例如指定浮点数的精度,或者整数的显示宽度。
- 小心使用 ENUM 和 SET 。虽然他们用起来很方便,但是不要滥用,否则有时候会变成陷阱。最好避免使用 BIT。
- 范式是好的,但是反范式(大多数情况下意味着重复数据)有时也是必须的,并且能带来好处。
- 最后 ALERT TABLE 在大部分情况下,它都会锁表并且重建整张表。
创建高性能的索引
索引(在 MySQL 中也叫"键"(key))是存储引擎用于快速找到记录的一种数据结构,这是索引的基本功能。索引对于良好的性能来说非常关键,尤其是当表中的数据量越来越大的时候,索引对于性能的影响愈发重要。在数据量较小且负载较低时,不恰当的索引对性能的影响可能还不明显,但当数据量逐渐增大时,性能会急剧下降。
索引优化应该是对查询性能优化最有效的手段了。索引能够将查询性能提高几个数量级。
索引基础
索引可以包含一个或多个列的值。如果索引包含多个列,那么列的顺序也十分重要,因为MySQL 只能有效地使用索引的最左前缀列。创建一个包含两个列的索引,和创建两个各包含一个列的索引大不相同。
索引的类型
在MySQL 中,索引是在存储引擎层而不是服务器层实现的。所以没有统一的索引标准:不同存储引擎的索引的工作方式是不一样的,也不是所有的存储引擎都支持所有类型的索引。即使多个存储引擎支持相同的索引,其底层实现原理也可能不同。
B-Tree 索引
B - Tree索引使用 B-Tree 数据结构来存储数据。大多数MySQL 存储引擎都支持这种索引。Archive 引擎是个例外:5.1 之前 Archive 引擎不支持任何索引,知道5.1 之后才支持单个自增列的索引。
我们使用术语“B-Tree”,是因为MySQL 在 CREATE TABLE 和其他语句中也只用该关键字。不过,底层的存储引擎也可能使用不同的存储结构,例如,NDB 集群存储引擎内部实际上使用了 T-Tree 结构存储这种索引,即使名字是 BTREE;InnoDB则使用的是 B+Tree。
存储引擎以不同的方式使用B-Tree索引,性能也各有不同,各有优势。例如,MyISAM使用前缀压缩技术使得索引更小,但InnoDB 则按照原数据格式进行存储。再如 MyISAM 索引通过数据的物理位置引用被索引的行,而InnoDB 使用主键引用被索引的行。
B-Tree 通常意味着所有的值都是按顺序存储的,并且根到每个叶子节点的距离相同。
B-Tree 索引能够加快访问数据的速度,因为存储引擎不在需要进行全表扫描来获取需要的数据,取而代之的是从索引的根节点开始进行搜索。根节点的槽中存放了指向子节点的指针,存储引擎根据这些指针向下查找。通过比较节点页的值和要找的值可以找到合适的指针进入下一层节点,这些指针实际上定义了子节点页中值的上界和下界。最终存储引擎要么找到相应的值,要么该值不存在。
叶子节点比较特别,他们的指针指向的是被索引的数据,而不是其他的节点页(不同类型的引擎“指针”类型不同)。
B-Tree 对索引列是顺序组织存储的,所以很合适查找范围数据。例如,在一个基于文本域的索引树上,按字母顺序传递连续的值,进行查找是非常合适的。
注意,索引对多个值进行排序的依据是 CREATE TABLE 语句中定义索引时列的顺序。
可以使用 B-Tree 索引的查询类型。B-Tree 索引适用于全键值,键值范围或键前缀查找。其中键前缀查找只适用于根据最左前缀的查找。前面所述的索引对如下类型的查询有效:
全值匹配
全值匹配指的是和索引中所有列进行匹配,
匹配最左前缀
对于多个列组成的索引,可以只使用索引的前几列。
匹配列前缀
也可以只匹配某一列的值的开头部分。例如对于索引第一列为姓的列,可以查找所有以K 开头的姓的人,这里也只使用了索引的第一列。
匹配范围值
上面提到的索引可以用于查找姓在 Aleen 和 blob之间的人。这里也只是用了索引的第一列。
精确匹配某一列并范围匹配另外一列
前面提到的索引的第二列为名,也可用于查找所有姓为 Aleen,并且名子是K 开头的人。即第一列姓全民匹配,第二列名范围匹配。
只访问索引的查询
B-Tree 通常可以支持“只访问索引的查询”,即查询只需要访问索引,而无需访问数据行。
因为索引树中的节点是有序的,所以除了按值查找之外,索引还可以用于查询中的 ORDER BY 操作。一般来说,如果B-Tree 可以按照某种方式查找到值,那么也可以按照这种方式用于排序。所以,如果 ORDER BY 子句满足前面列出的几种查询类型,则这个索引也可以满足对应的排序需求。
下面是关于B-Tree的一些限制:
- 如果不是按照索引的最左开始查找,则无法使用索引。
- 不能跳过索引中的列。
- 如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找。
索引的顺序是非常重要的,这些限制都和索引的列有关。在优化性能的时候,可能需要使用相同的列但顺序不同的索引来满足不同类型的查询需求。
哈希索引
哈希索引是基于哈希表的实现,只有精确匹配索引的所有列才有效。对于每一行数据,存储引擎都会对所有的索引列计算下一个哈希码,哈希索引将所有的哈希码存储在索引中,同时在哈希表中,保存指向每个数据行的指针。
在MySQL 中,只有Memory 引擎显示支持哈希索引。这也是Memory 表的默认索引类型,Memory 引擎同时也支持B-Tree索引。值得一提的是,Memory 引擎是支持非唯一哈希索引的,这在数据库世界里是与众不同的,如果多个列的哈希值相同,索引会以链表的方式存放多个记录指针到同一个哈希条目中。
因为索引自身只需存储相应的哈希值,所以索引的结构十分紧凑,这也让Hash索引的速度非常快。然而哈希索引也有它的限制:
- 哈希索引只包含哈希值和行指针,而不存储字段值,索引不能通过读取索引中的值来避免读取行。不过访问内存中的行的速度很快,所以大部分情况下这一点对性能的影响并不明显。
- 哈希索引数据不是按照索引值顺序存储的,所以也就无法用于排序。
- 哈希索引也不支持部分索引匹配列查找,因为哈希索引始终是使用索引列的全部内容来计算哈希值的。
- 哈希索引只支持等值比较查询,包括
= 、IN()、<=>
也不支持任何范围查询。 - 访问哈希索引的数据非常快,除非有很多的哈