惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

I
InfoQ
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Apple Machine Learning Research
Apple Machine Learning Research
月光博客
月光博客
B
Blog
罗磊的独立博客
GbyAI
GbyAI
博客园 - 三生石上(FineUI控件)
雷峰网
雷峰网
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Microsoft Security Blog
Microsoft Security Blog
宝玉的分享
宝玉的分享
The GitHub Blog
The GitHub Blog
人人都是产品经理
人人都是产品经理
博客园 - Franky
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
博客园 - 聂微东
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
Visual Studio Blog
MyScale Blog
MyScale Blog
Google DeepMind News
Google DeepMind News
G
Google Developers Blog
aimingoo的专栏
aimingoo的专栏

Jiajun的技术笔记

你好,2026! TiDB 源码阅读(六):TiDB Coprocessor 源码解析 性能优化的核心思想 TiDB 源码阅读(五):索引 TiDB 源码阅读(四):AST、逻辑计划、物理计划 CockroachDB Serverless Architecture podman 无故退出 Cursor Control-L (CTRL-L) Keyboard Shortcuts in Terminal Replace docker with podman Using xmonad with xfce4 A RC script for freebsd frpc 自己动手写一个k8s controller AI 会取代你的(编程)岗位吗? 自建DERP服务器提升Tailscale连接速度(使用Nginx转发) 自动升级Docker容器 再读《程序员修炼之道-从小工到专家》 让浏览器下载文件 再读《软件随想录》/《黑客与画家》/《软技能》 HTTP 压力测试中的 Coordinated Omission 2的补码 编程语言中的 context 是什么? flutter macOS 构建出错 Flatpak 使用小记 Golang CAS 操作是怎么实现的 PostgreSQL 当MQ来使用 Clash 结合 工作VPN 的网络设计 使用 PostgreSQL 搭建 JuiceFS PostgreSQL 配置优化和日志分析 有GitHub Copilot?那就可以搭建你的ChatGPT4服务 窗口函数的使用(以PG为例)
数据库索引设计与优化
Jiajun Huang · 2018-05-13 · via Jiajun的技术笔记

数据库索引设计与优化

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

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

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

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

    mysql [email protected]: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树是有序的,所以可以减少很多无关数据。

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



相关文章