LabHub
学习 学习路径 课程

数据库概念

建索引,并确认计划有没有变

在 LabHub 中继续学习

目标

亲手创建多种索引,并直接观察执行计划如何变化。本实验的目的不是只学会创建索引,而是理解索引何时会被使用,何时会被忽略

为什么重要

创建索引后执行计划仍未改变时,最常听到的建议就是强制使用索引。这不是诊断,而是在压制症状;实际上,强制使用索引反而变慢的情况非常多。因为索引扫描会为每一条匹配记录随机访问堆页面,当返回行很多时,就相当于以随机顺序读取整张表。

因此,索引未被使用时,需要区分两个问题:**是优化器的判断正确,所以没有使用索引;还是查询的形式根本无法使用索引。**前者需要更换或放弃索引,后者则需要修改查询。两者的应对方式完全相反,所以必须先做区分。

本实验还会创建部分索引、表达式索引和覆盖索引,帮助你形成‘索引并非只有一种,而是有多种类型’的直观认识。

步骤

  1. 不要把 EXPLAIN SELECT count(*) FROM orders WHERE channel = 'partner'; 的输出保存到 /root/exp,而要保存到 /root/plan_seq.txt。结果应为顺序扫描。
  2. orders (ordered_at) 上创建名为 idx_orders_ordered_at 的 B 树索引。
  3. 将对 ordered_at 使用范围条件的查询的 EXPLAIN 输出保存到 /root/plan_idx.txt。计划中必须出现 idx_orders_ordered_at
  4. order_items 上创建名为 idx_order_items_order_product 的复合索引,列顺序为 (order_id, product_id)
  5. orders (ordered_at) 上创建带有 status = 'pending' 条件的部分索引 idx_orders_pending
  6. customers 上为 lower(email) 创建表达式索引 idx_customers_email_lower
  7. products 上创建覆盖索引:键列为 (category, price),INCLUDE 列为 name,索引名为 idx_products_cat_price
  8. 禁用顺序扫描后,将根据 ordered_at 条件计算 count(*) 的 EXPLAIN 输出保存到 /root/plan_only.txt。结果应出现 Index Only Scan

提示

保存没有索引时的计划

不要把 EXPLAIN SELECT count(*) FROM orders WHERE channel = 'partner'; 的输出保存到 /root/exp,而要保存到 /root/plan_seq.txt。结果应为顺序扫描。

只需把 EXPLAIN 放在查询前面即可。必须使用尚无索引的列作为条件,才会出现顺序扫描。

创建基本 B 树索引

orders (ordered_at) 上创建名为 idx_orders_ordered_at 的 B 树索引。

形式为 CREATE INDEX 名称 ON 表 (列)。索引名称必须完全一致才能通过评分。

保存使用索引的计划

将对 ordered_at 使用范围条件的查询的 EXPLAIN 输出保存到 /root/plan_idx.txt。计划中必须出现 idx_orders_ordered_at

范围条件必须足够窄,优化器才会选择索引。如果对列应用函数,就无法使用这个索引。

确定复合索引的列顺序

order_items 上创建名为 idx_order_items_order_product 的复合索引,列顺序为 (order_id, product_id)

经常用于等值条件的列应放在前面。顺序改变后就是另一个索引。

用部分索引减小体积

orders (ordered_at) 上创建带有 status = 'pending' 条件的部分索引 idx_orders_pending

使用 CREATE INDEX ... WHERE 条件这种形式,可以只在索引中保存部分行。请比较索引大小。

创建表达式索引

customers 上为 lower(email) 创建表达式索引 idx_customers_email_lower

可以根据计算结果而不是列本身创建索引。表达式必须用括号括住,查询也必须使用完全相同的表达式,索引才会生效。

用覆盖索引减少堆访问

products 上创建覆盖索引:键列为 (category, price),INCLUDE 列为 name,索引名为 idx_products_cat_price

有一种子句可以分别定义用于查找的键列和只在结果中需要的列。

观察 Index Only Scan

禁用顺序扫描后,将根据 ordered_at 条件计算 count(*) 的 EXPLAIN 输出保存到 /root/plan_only.txt。结果应出现 Index Only Scan

表很小时,优化器会选择顺序扫描。请仅为诊断暂时禁用顺序扫描,然后重新生成计划。