数据库索引设计与优化

栏目: 数据库 · 发布时间: 7年前

内容简介:这是读完了 <高性能MySQL> , <数据库索引设计与优化> 和 MySQL Manual 之后的总结。本文的所有知识都是与MySQL相关的,其他 数据库也许有不同。诸如AND, OR等语句,MySQL可能会使用多个索引检索出结果,然后对多个结果进行AND,OR等操作。符合三星的查询是最佳的查询,但是实际过程我们往往难以做到,例如如果使用了ORM,一般都没有可能拿到第三颗星。

这是读完了 <高性能MySQL> , <数据库索引设计与优化> 和 MySQL Manual 之后的总结。本文的所有知识都是与 MySQL 相关的,其他 数据库也许有不同。

  • 索引的本质是用写入时间换取查询时间

  • 所有的优化都是有有效上限的,例如内存当然是越大越好,但是当你把所有的数据和缓存等都装下了之后,内存再往上增加就不会再 有明显的所用。CPU当然是越快越好,但是如果你的查询不是CPU bound,那么也不会有明显作用。磁盘随机IO当然是越快越好,也许有一天 磁盘可以达到和内存相当的速度,那时候磁盘也许就不再是瓶颈。

  • 通常情况下来说,MySQL只能使用一颗索引树,但是5.0之后有索引合并的优化,这种情况下可以使用多个索引,例如:

mysql jiajun@127.0.0.1:fd_test> EXPLAIN SELECT * FROM user WHERE mobile='12345678910' OR fullname='小白';
+------+---------------+---------+-------------+---------------------+---------------------+-----------+--------+--------+-----------------------------------------------+
| id   | select_type   | table   | type        | possible_keys       | key                 | key_len   | ref    | rows   | Extra                                         |
|------+---------------+---------+-------------+---------------------+---------------------+-----------+--------+--------+-----------------------------------------------|
| 1    | SIMPLE        | user    | index_merge | MOBILE_IDX,IDX_NAME | MOBILE_IDX,IDX_NAME | 767,768   | <null> | 2      | Using union(MOBILE_IDX,IDX_NAME); Using where |
+------+---------------+---------+-------------+---------------------+---------------------+-----------+--------+--------+-----------------------------------------------+
1 row in set
Time: 0.010s

诸如AND, OR等语句,MySQL可能会使用多个索引检索出结果,然后对多个结果进行AND,OR等操作。

  • 三星索引

    • 第一颗星在 WHERE 语句的所有条件。如果该条件可以使用一个索引(假设有多个condition,则应当有一个联合索引),这样可以 加快过滤的速度
    • 第二颗星在 ORDER BY 语句。详见文章最下面的引用。使用例如 SELECT * FROM user ORDER BY mobile LIMIT 5 这样的查询 时,MySQL可以直接从 mobile 的索引进行查找,充分的利用了 mobile 索引的有序性。
    • 第三颗星在 SELECT 后面的字段,如果 SELECT 所取出来的字段都是索引里的值,那么就无需从真正的数据里获取到指定的 那一行来获取所需要的数据了。

符合三星的查询是最佳的查询,但是实际过程我们往往难以做到,例如如果使用了ORM,一般都没有可能拿到第三颗星。

  • 我们来假装自己是MySQL服务器,模拟一下查询的过程。首先收到请求校验请求等这些过程我们就先忽略,直接看到利用索引的 这个过程。

    • 优化器决定是否使用某个索引
    • 使用某个索引之后,索引通常是B树或者其变种。通常根节点一定在内存中,其下层的节点可能会在内存中也可能会在磁盘上,取决 于操作系统内存是否充足以及是否将其缓存。
      • 假设将其缓存在内存中,则一步一步往下查找,在内存中自然速度是非常快的,一直到找到叶子节点
      • 假设没有缓存在内存中,则找到子节点所在的磁盘中的块,读取数据,然后继续进行,一直找到叶子节点
      • 此前的步骤中,数据往往是连续的。找到叶子节点之后,读取叶子节点中的数据,找到数据真正所在的行,这一步往往是 随机I/O。因为数据所存储的地方可能是不同的。
        • 但是主键通常会是一张表中有且仅有的聚族索引。聚族索引是连续存储的索引。
  • 联合索引是个好东西,但是使用联合索引查询时,会遵循最左匹配原则,例如有个联合索引是 (key1, key2, key3) ,那么能用上 的查找是 (key1)(key1, key2)(key1, key2, key3) 。其实这里我有一点不明白,其实索引可以特殊处理一下,然后可以跳 过其中某个值进行使用,例如 (key1, key3) ,原因是无论是 int还是varchar都可以换算成大小固定的值,例如int是4 byte, varchar换成一个指针,这样就可以进行固定大小的偏移从而跨值利用索引。也许是为了降低复杂度所以目前并没有这样做,也许是有 其他原因。

另外为什么是最左匹配原则呢?因为比较索引的时候是从左往右比较。

  • JOIN 是怎么使用索引的?最朴素的JOIN自然是 for...for 嵌套,例如 SELECT A.* FROM A JOIN B ON A.key1 = B.key1 ,可能会 生成类似于
def query():
    result = []

    for row_in_b in B:
        for row_in_a in A:
            if row_in_b.key1 == row_in_a.key1:
                result.append(row_in_a)

    return result

这样的代码,如果在连表的字段上有索引,那么可能会生成:

def query():
    result = []

    for row_in_b in B:
        if row_in_b.key1 in A.key1:
            row_in_a = [i for i in A where A.key1 = row_in_b.key1]
            result.extend(row_in_a)

    return result

从而把时间复杂度从 O(n^2) 降低到 O(n lgn)

  • IN 语句是怎么使用索引的?MySQL会把 IN 后面的条件先排序,然后进行范围查找。因为B树是有序的,所以可以减少很多无关数据。

还有很多。。。不过不想写了。。。还是看书吧,哈哈哈


以上所述就是小编给大家介绍的《数据库索引设计与优化》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Game Programming Patterns

Game Programming Patterns

Robert Nystrom / Genever Benning / 2014-11-2 / USD 39.95

The biggest challenge facing many game programmers is completing their game. Most game projects fizzle out, overwhelmed by the complexity of their own code. Game Programming Patterns tackles that exac......一起来看看 《Game Programming Patterns》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具