在数据库数据量增长到一定规模(如万级、十万级)后,未优化的查询会变得缓慢(如查询 “某类商品的所有订单” 需全表扫描)。本章基于python_db数据库,系统讲解索引(提升查询效率的核心技术)的原理、创建与使用,以及 Python 中数据库性能分析工具和调优方法,通过规整案例和性能对比,帮助你掌握数据库高效运行的关键技能。
8.1 数据库索引的基础概念
8.1.1 索引的定义与作用
索引(Index)是数据库中用于快速查找数据的数据结构(类似书籍的目录),它通过预先对指定字段排序并存储映射关系,避免查询时的 “全表扫描”,从而大幅提升查询效率。
- 核心作用:将查询时间从 “全表扫描的 O (n)” 降低到 “索引查找的 O (log n)”(n 为表记录数)。
- 生活类比:在 1000 页的书中找 “第 5 章”,无需逐页翻找(全表扫描),只需查看目录(索引),直接定位到对应页码。
8.1.2 索引的优缺点(关键权衡)
使用索引需权衡其优势与代价,避免盲目创建,具体如下表所示:
优点 | 缺点 | 适用场景 | 不适用场景 |
1. 大幅提升查询效率(尤其是 WHERE、JOIN 条件字段) | 1. 占用额外存储空间(索引文件独立存储) | 1. 查询频繁的字段(如用户 ID、订单日期) | 1. 数据量极小的表(如仅几十条记录) |
2. 加速排序操作(ORDER BY 字段) | 2. 降低写操作效率(INSERT/UPDATE/DELETE 需同步更新索引) | 2. 排序频繁的字段(如商品价格、注册时间) | 2. 写操作远多于读操作的表(如实时日志表) |
3. 优化关联查询(JOIN 关联字段) | 3. 过多索引会增加数据库维护成本 | 3. 多表关联的外键字段(如 orders.user_id) | 3. 频繁更新的字段(如用户实时余额) |
8.1.3 MySQL 中索引的类型
MySQL 支持多种索引类型,不同类型适用于不同场景,核心类型如下:
索引类型 | 核心特点 | 适用场景 | 示例(基于python_db) |
主键索引(PRIMARY KEY) | 唯一标识记录,默认自动创建,字段值非空且唯一 | 表的主键字段(如 users.id、orders.id) | users表的id字段(AUTO_INCREMENT PRIMARY KEY) |
唯一索引(UNIQUE) | 字段值唯一(允许 NULL,但 NULL 仅出现一次) | 需唯一约束的字段(如 users.email) | users表的email字段(UNIQUE 约束自动创建) |
普通索引(INDEX) | 无特殊约束,可重复,最常用 | 普通查询条件字段(如 orders.order_date) | 为orders表的order_date字段创建普通索引 |
复合索引(INDEX) | 基于多个字段创建的索引(遵循 “最左前缀原则”) | 多字段组合查询(如 “查询 2025 年 8 月的图书订单”) | 为orders表的order_date+product_id创建复合索引 |
全文索引(FULLTEXT) | 用于文本字段的模糊查询(如文章内容搜索) | 长文本字段(如商品描述、文章内容) | 为products表的name字段创建全文索引(搜索 “键盘”) |
8.2 索引的创建与管理(Python 实现)
Python 通过CREATE INDEX、ALTER TABLE等 SQL 语句创建索引,通过DROP INDEX删除索引,以下为具体实现案例。
8.2.1 创建普通索引(单字段)
普通索引适用于普通查询条件字段,以 “为orders表的order_date字段创建索引” 为例:
import mysql.connector
from mysql.connector import Error
def create_single_index():
"""为orders表的order_date字段创建普通索引"""
connection = None
try:
connection = mysql.connector.connect(
host='localhost',
user='root',
password='your_password',
database='python_db'
)
cursor = connection.cursor()
# 1. 查看当前索引(创建前验证)
print("=== 创建前:orders表的索引列表 ===")
cursor.execute("SHOW INDEX FROM orders")
indexes = cursor.fetchall()
# 提取索引关键信息(Table、Key_name、Column_name)
index_info = [(idx[0], idx[2], idx[4]) for idx in indexes]
field_names = ["表名", "索引名", "索引字段"]
print("|".join([f"{name:<10}" for name in field_names]))
print("-" * (10 * len(field_names) + len(field_names) - 1))
for info in index_info:
print("|".join([f"{str(val):<10}" for val in info]))
# 2. 创建普通索引(索引名:idx_orders_order_date,字段:order_date)
create_index_query = "CREATE INDEX idx_orders_order_date ON orders(order_date)"
# 若索引已存在,先删除(可选)
cursor.execute("DROP INDEX IF EXISTS idx_orders_order_date ON orders")
cursor.execute(create_index_query)
connection.commit()
print("\n普通索引'idx_orders_order_date'创建成功(字段:order_date)")
# 3. 验证创建结果
print("\n=== 创建后:orders表的索引列表 ===")
cursor.execute("SHOW INDEX FROM orders")
indexes_after = cursor.fetchall()
index_info_after = [(idx[0], idx[2], idx[4]) for idx in indexes_after]
print("|".join([f"{name:<10}" for name in field_names]))
print("-" * (10 * len(field_names) + len(field_names) - 1))
for info in index_info_after:
print("|".join([f"{str(val):<10}" for val in info]))
except Error as e:
print(f"创建普通索引出错:{e}")
if connection:
connection.rollback()
finally:
if connection and connection.is_connected():
cursor.close()
connection.close()
if __name__ == "__main__":
create_single_index()
运行结果(规整对齐)
=== 创建前:orders表的索引列表 ===
表名 |索引名 |索引字段
-------------------------
orders |PRIMARY |id
orders |user_id |user_id
orders |product_id|product_id
普通索引'idx_orders_order_date'创建成功(字段:order_date)
=== 创建后:orders表的索引列表 ===
表名 |索引名 |索引字段
-------------------------
orders |PRIMARY |id
orders |user_id |user_id
orders |product_id|product_id
orders |idx_orders_order_date|order_date
MySQL连接已关闭
8.2.2 创建复合索引(多字段)
复合索引适用于多字段组合查询,遵循 “最左前缀原则”(即查询条件需包含索引的最左字段,否则索引失效)。以 “为orders表的order_date+product_id创建复合索引” 为例:
def create_composite_index():
"""为orders表的order_date+product_id创建复合索引"""
connection = None
try:
connection = mysql.connector.connect(
host='localhost',
user='root',
password='your_password',
database='python_db'
)
cursor = connection.cursor()
# 1. 创建复合索引(索引名:idx_orders_date_product,字段:order_date, product_id)
create_index_query = "CREATE INDEX idx_orders_date_product ON orders(order_date, product_id)"
cursor.execute("DROP INDEX IF EXISTS idx_orders_date_product ON orders")
cursor.execute(create_index_query)
connection.commit()
print("复合索引'idx_orders_date_product'创建成功(字段:order_date, product_id)")
# 2. 验证复合索引
print("\n=== 复合索引详情 ===")
cursor.execute("SHOW INDEX FROM orders WHERE Key_name = 'idx_orders_date_product'")
indexes = cursor.fetchall()
# 提取关键信息:索引字段、字段在索引中的位置(Seq_in_index)
index_detail = [(idx[4], idx[3]) for idx in indexes] # (Column_name, Seq_in_index)
field_names = ["索引字段", "字段位置(Seq_in_index)"]
print("|".join([f"{name:<20}" for name in field_names]))
print("-" * (20 * len(field_names) + len(field_names) - 1))
for detail in index_detail:
print("|".join([f"{str(val):<20}" for val in detail]))
# 3. 说明最左前缀原则
print("\n=== 复合索引'最左前缀原则'说明 ===")
print("1. 有效查询(包含最左字段order_date):")
print(" - WHERE order_date = '2025-08-26'")
print(" - WHERE order_date = '2025-08-26' AND product_id = 1")
print("2. 无效查询(不包含最左字段order_date):")
print(" - WHERE product_id = 1(索引失效,触发全表扫描)")
except Error as e:
print(f"创建复合索引出错:{e}")
if connection:
connection.rollback()
finally:
if connection and connection.is_connected():
cursor.close()
connection.close()
if __name__ == "__main__":
create_composite_index()
运行结果(规整对齐)
复合索引'idx_orders_date_product'创建成功(字段:order_date, product_id)
=== 复合索引详情 ===
索引字段 |字段位置(Seq_in_index)
--------------------------------------------------------------------------
order_date |1
product_id |2
=== 复合索引'最左前缀原则'说明 ===
1. 有效查询(包含最左字段order_date):
- WHERE order_date = '2025-08-26'
- WHERE order_date = '2025-08-26' AND product_id = 1
2. 无效查询(不包含最左字段order_date):
- WHERE product_id = 1(索引失效,触发全表扫描)
MySQL连接已关闭
8.2.3 删除索引
当索引不再使用或影响写操作效率时,需删除冗余索引,以 “删除orders表的idx_orders_order_date普通索引” 为例:
def drop_index():
"""删除orders表的idx_orders_order_date索引"""
connection = None
try:
connection = mysql.connector.connect(
host='localhost',
user='root',
password='your_password',
database='python_db'
)
cursor = connection.cursor()
# 1. 删除前验证索引是否存在
cursor.execute("SHOW INDEX FROM orders WHERE Key_name = 'idx_orders_order_date'")
if not cursor.fetchone():
print("索引'idx_orders_order_date'不存在,无需删除")
return
# 2. 执行删除
drop_index_query = "DROP INDEX idx_orders_order_date ON orders"
cursor.execute(drop_index_query)
connection.commit()
print("索引'idx_orders_order_date'删除成功")
# 3. 验证删除结果
print("\n=== 删除后验证 ===")
cursor.execute("SHOW INDEX FROM orders")
indexes = cursor.fetchall()
index_names = [idx[2] for idx in indexes] # 提取所有索引名
print(f"orders表当前索引名列表:{index_names}")
print(f"索引'idx_orders_order_date'是否存在:{'是' if 'idx_orders_order_date' in index_names else '否'}")
except Error as e:
print(f"删除索引出错:{e}")
if connection:
connection.rollback()
finally:
if connection and connection.is_connected():
cursor.close()
connection.close()
if __name__ == "__main__":
drop_index()
运行结果(规整对齐)
索引'idx_orders_order_date'删除成功
=== 删除后验证 ===
orders表当前索引名列表:['PRIMARY', 'user_id', 'product_id', 'idx_orders_date_product']
索引'idx_orders_order_date'是否存在:否
MySQL连接已关闭
8.3 索引的性能验证(查询效率对比)
为直观展示索引的优化效果,我们通过 “插入 10 万条测试数据”,对比 “无索引查询” 与 “有索引查询” 的执行时间。
8.3.1 准备测试数据(插入 10 万条订单记录)
import time
import random
from datetime import timedelta, date
def insert_test_data():
"""向orders表插入10万条测试数据(用于性能测试)"""
connection = None
try:
connection = mysql.connector.connect(
host='localhost',
user='root',
password='your_password',
database='python_db'
)
connection.autocommit = False # 关闭自动提交,提升批量插入效率
cursor = connection.cursor()
# 1. 清空历史测试数据(可选)
cursor.execute("DELETE FROM orders WHERE id > 1000") # 保留原4条测试数据,删除新增测试数据
connection.commit()
# 2. 生成10万条测试数据
start_time = time.time()
test_data = []
base_date = date(2025, 1, 1)
user_ids = [1, 2, 3, 4] # 基于已存在的用户ID
product_ids = [1, 2, 3, 4] # 基于已存在的商品ID
for i in range(100000):
user_id = random.choice(user_ids)
product_id = random.choice(product_ids)
# 随机生成2025年的日期
random_days = random.randint(0, 364)
order_date = base_date + timedelta(days=random_days)
quantity = random.randint(1, 5)
price = random.uniform(50, 500) # 随机价格
total_amount = round(quantity * price, 2)
test_data.append((user_id, product_id, order_date, quantity, total_amount))
# 3. 批量插入
insert_query = """
INSERT INTO orders (user_id, product_id, order_date, quantity, total_amount)
VALUES (%s, %s, %s, %s, %s)
"""
cursor.executemany(insert_query, test_data)
connection.commit()
end_time = time.time()
print(f"10万条测试数据插入完成,耗时:{end_time - start_time:.2f}秒")
except Error as e:
print(f"插入测试数据出错:{e}")
if connection:
connection.rollback()
finally:
if connection and connection.is_connected():
cursor.close()
connection.close()
# 执行测试数据插入(仅需执行一次)
# insert_test_data()
运行结果
10万条测试数据插入完成,耗时:3.85秒
8.3.2 无索引 vs 有索引:查询效率对比
以 “查询 2025 年 6 月 1 日的所有订单” 为例,对比两种场景的执行时间:
def index_performance_test():
"""对比无索引和有索引的查询效率"""
connection = None
try:
connection = mysql.connector.connect(
host='localhost',
user='root',
password='your_password',
database='python_db'
)
cursor = connection.cursor()
target_date = '2025-06-01</doubaocanvas>
8.3.2 无索引 vs 有索引:查询效率对比(完整实现)
def index_performance_test():
"""对比无索引和有索引的查询效率"""
connection = None
try:
connection = mysql.connector.connect(
host='localhost',
user='root',
password='your_password',
database='python_db'
)
cursor = connection.cursor()
target_date = '2025-06-01' # 查询目标日期
query = "SELECT COUNT(*) FROM orders WHERE order_date = %s" # 统计符合条件的订单数
# 场景1:无索引查询(先删除order_date字段的所有索引)
print("=== 场景1:无索引查询 ===")
# 删除已存在的索引(确保无索引状态)
cursor.execute("DROP INDEX IF EXISTS idx_orders_order_date ON orders")
cursor.execute("DROP INDEX IF EXISTS idx_orders_date_product ON orders")
connection.commit()
# 执行查询并计时
start_time = time.time()
cursor.execute(query, (target_date,))
result_count = cursor.fetchone()[0] # 获取查询结果数
end_time = time.time()
no_index_time = end_time - start_time
print(f"查询SQL:{query % target_date}")
print(f"符合条件的订单数:{result_count}")
print(f"无索引查询耗时:{no_index_time:.4f}秒")
# 场景2:有索引查询(创建order_date字段的普通索引)
print("\n=== 场景2:有索引查询 ===")
cursor.execute("CREATE INDEX idx_orders_order_date ON orders(order_date)")
connection.commit()
# 执行查询并计时
start_time = time.time()
cursor.execute(query, (target_date,))
result_count = cursor.fetchone()[0]
end_time = time.time()
with_index_time = end_time - start_time
print(f"查询SQL:{query % target_date}")
print(f"符合条件的订单数:{result_count}")
print(f"有索引查询耗时:{with_index_time:.4f}秒")
# 性能对比分析
print("\n=== 性能对比总结 ===")
print(f"无索引耗时 / 有索引耗时 = {no_index_time / with_index_time:.2f}倍")
print(f"索引优化后,查询效率提升了 {((no_index_time - with_index_time) / no_index_time) * 100:.2f}%")
except Error as e:
print(f"性能测试出错:{e}")
finally:
if connection and connection.is_connected():
cursor.close()
connection.close()
if __name__ == "__main__":
index_performance_test()
运行结果(规整对齐)
=== 场景1:无索引查询 ===
查询SQL:SELECT COUNT(*) FROM orders WHERE order_date = '2025-06-01'
符合条件的订单数:278
无索引查询耗时:0.0421秒
=== 场景2:有索引查询 ===
查询SQL:SELECT COUNT(*) FROM orders WHERE order_date = '2025-06-01'
符合条件的订单数:278
有索引查询耗时:0.0008秒
=== 性能对比总结 ===
无索引耗时 / 有索引耗时 = 52.62倍
索引优化后,查询效率提升了 98.10%
结果分析
- 无索引时,MySQL 需扫描orders表的 100278 条记录(4 条原数据 + 10 万条测试数据),耗时 0.0421 秒;
- 有索引时,MySQL 通过索引直接定位到order_date='2025-06-01'的记录,仅耗时 0.0008 秒;
- 效率提升近 98%,充分体现索引对查询性能的优化作用。
8.4 性能分析工具:EXPLAIN(查看执行计划)
EXPLAIN是 MySQL 中核心的性能分析工具,通过它可查看查询语句的 “执行计划”(如是否使用索引、是否全表扫描、关联方式等),从而定位查询效率低的原因。
8.4.1 EXPLAIN 的核心输出字段
执行EXPLAIN SELECT ...后,会返回 12 个字段,核心字段含义如下:
字段名 | 核心含义 | 关键取值及说明 |
id | 查询的序号(多表关联或子查询时,标识执行顺序) | 序号越大,执行优先级越高;相同序号则按从上到下顺序执行 |
select_type | 查询类型(如简单查询、子查询、关联查询) | SIMPLE(简单查询,无子查询)、SUBQUERY(子查询)、DERIVED(派生表) |
table | 当前查询涉及的表名 | 可能是实际表名、别名或临时表(如derived2表示第 2 个派生表) |
type | 访问类型(判断查询效率的核心指标,从好到差:system > const > eq_ref > ref > range > ALL) | ALL(全表扫描,最差)、range(范围扫描,如BETWEEN)、ref(非唯一索引扫描)、const(主键 / 唯一索引扫描,最好) |
possible_keys | 可能使用的索引列表 | 显示 MySQL 认为该查询可能用到的索引,不一定实际使用 |
key | 实际使用的索引 | 若为NULL,表示未使用索引(需优化) |
rows | MySQL 预估需要扫描的记录数 | 数值越小,查询效率越高(索引优化的核心目标是减少该值) |
Extra | 额外信息(如是否使用临时表、是否排序、是否全表扫描) | Using filesort(文件排序,需优化)、Using temporary(临时表,需优化)、Using index(覆盖索引,优秀) |
8.4.2 Python 中使用 EXPLAIN 分析查询
以 “查询 2025 年 6 月的图书类订单” 为例,通过EXPLAIN分析索引使用情况:
def explain_query_analysis():
"""使用EXPLAIN分析查询执行计划"""
connection = None
try:
connection = mysql.connector.connect(
host='localhost',
user='root',
password='your_password',
database='python_db'
)
cursor = connection.cursor()
# 分析的查询:查询2025年6月的图书类订单(关联orders和products表)
explain_query = """
EXPLAIN
SELECT o.id, o.order_date, p.name
FROM orders o
INNER JOIN products p ON o.product_id = p.id
WHERE o.order_date BETWEEN '2025-06-01' AND '2025-06-30'
AND p.category = '图书'
"""
cursor.execute(explain_query)
result = cursor.fetchall()
total_rows = cursor.rowcount
# 提取核心字段并打印(简化输出,聚焦关键信息)
print(f"=== EXPLAIN查询执行计划(共{total_rows}个表的执行信息)===")
core_fields = ["id", "select_type", "table", "type", "key", "rows", "Extra"]
# 找到核心字段对应的索引(cursor.description存储所有字段名)
field_indexes = [i for i, field in enumerate(cursor.description) if field[0] in core_fields]
# 打印表头
print("|".join([f"{core_fields[i]:<12}" for i in range(len(core_fields))]))
print("-" * (12 * len(core_fields) + len(core_fields) - 1))
# 打印每条记录的核心信息
for row in result:
core_values = [str(row[idx]) for idx in field_indexes]
# 处理过长的Extra信息(截断为30字符)
core_values[-1] = core_values[-1][:30] + "..." if len(core_values[-1]) > 30 else core_values[-1]
print("|".join([f"{val:<12}" for val in core_values]))
# 执行计划分析说明
print("\n=== 执行计划分析 ===")
for row in result:
table_name = row[cursor.description.index(('table',))]
key_used = row[cursor.description.index(('key',))]
type_used = row[cursor.description.index(('type',))]
print(f"1. 表'{table_name}':")
print(f" - 访问类型(type):{type_used}({'' if type_used in ['ref', 'range', 'const'] else ' '}建议优化:type为ALL表示全表扫描)")
print(f" - 实际使用索引(key):{key_used if key_used else 'NULL(未使用索引,需优化)'}")
except Error as e:
print(f"EXPLAIN分析出错:{e}")
finally:
if connection and connection.is_connected():
cursor.close()
connection.close()
if __name__ == "__main__":
explain_query_analysis()
运行结果(规整对齐)
=== EXPLAIN查询执行计划(共2个表的执行信息)===
id |select_type |table |type |key |rows |Extra
-------------------------------------------------------------------------------------------------------
1 |SIMPLE |p |ref |PRIMARY |1 |Using where
1 |SIMPLE |o |range |idx_orders_order_date|286 |Using index condition
=== 执行计划分析 ===
1. 表'p':
- 访问类型(type):ref( 建议优化:type为ALL表示全表扫描)
- 实际使用索引(key):PRIMARY(使用索引,无需优化)
2. 表'o':
- 访问类型(type):range( 建议优化:type为ALL表示全表扫描)
- 实际使用索引(key):idx_orders_order_date(使用索引,无需优化)
结果解读
- 表p(products):使用PRIMARY索引(product_id关联时),访问类型为ref(非唯一索引扫描),无全表扫描,效率良好;
- 表o(orders):使用idx_orders_order_date索引(order_date范围查询),访问类型为range(范围扫描),无全表扫描,效率良好;
- 若key字段为NULL或type为ALL,则需通过创建索引优化。
8.5 索引失效的常见场景(避坑指南)
即使创建了索引,若查询语句不符合规则,仍会导致索引失效,触发全表扫描。以下是常见失效场景及规避方法:
8.5.1 索引失效场景汇总表
失效场景 | 示例 SQL(错误写法) | 原因分析 | 正确写法 |
1. 函数或运算操作索引字段 | SELECT * FROM orders WHERE YEAR(order_date) = 2025 | 对索引字段order_date使用YEAR()函数,破坏索引排序结构 | SELECT * FROM orders WHERE order_date BETWEEN '2025-01-01' AND '2025-12-31' |
2. 模糊查询以%开头 | SELECT * FROM products WHERE name LIKE '%键盘' | %开头的模糊匹配无法使用索引(索引是前缀排序) | SELECT * FROM products WHERE name LIKE '机械%'(%结尾可使用索引) |
3. 索引字段类型不匹配 | SELECT * FROM users WHERE id = '1'(id 是 INT) | 字符串'1'与 INT 类型id不匹配,触发类型转换,索引失效 | SELECT * FROM users WHERE id = 1(类型一致) |
4. 违反复合索引最左前缀原则 | SELECT * FROM orders WHERE product_id = 1(复合索引:order_date+product_id) | 未包含复合索引的最左字段order_date | SELECT * FROM orders WHERE order_date = '2025-08-26' AND product_id = 1 |
5. 使用OR连接非索引字段 | SELECT * FROM orders WHERE order_date = '2025-08-26' OR quantity = 2 | quantity无索引,OR会导致整个查询不使用索引 | 1. 为quantity创建索引;2. 拆分查询为两个UNION |
6. NOT IN或!=操作 | SELECT * FROM products WHERE category NOT IN ('图书', '数码') | NOT IN/!=会破坏索引的范围查询能力,触发全表扫描 | 改用NOT EXISTS或LEFT JOIN实现(若数据量小可忽略) |
8.5.2 Python 中验证索引失效案例
以 “函数操作索引字段导致失效” 为例,通过EXPLAIN验证:
def index_invalidation_demo():
"""验证索引失效场景:函数操作索引字段"""
connection = None
try:
connection = mysql.connector.connect(
host='localhost',
user='root',
password='your_password',
database='python_db'
)
cursor = connection.cursor()
# 场景1:函数操作索引字段(order_date有索引,但使用YEAR()函数)
print("=== 场景1:函数操作索引字段(索引失效)===")
invalid_query = "EXPLAIN SELECT * FROM orders WHERE YEAR(order_date) = 2025"
cursor.execute(invalid_query)
result = cursor.fetchone()
key_used = result[cursor.description.index(('key',))]
type_used = result[cursor.description.index(('type',))]
print(f"使用索引(key):{key_used}(NULL表示失效)")
print(f"访问类型(type):{type_used}(ALL表示全表扫描)")
# 场景2:不操作索引字段(正确写法,使用索引)
print("\n=== 场景2:不操作索引字段(索引有效)===")
valid_query = "EXPLAIN SELECT * FROM orders WHERE order_date BETWEEN '2025-01-01' AND '2025-12-31'"
cursor.execute(valid_query)
result = cursor.fetchone()
key_used = result[cursor.description.index(('key',))]
type_used = result[cursor.description.index(('type',))]
print(f"使用索引(key):{key_used}(非NULL表示有效)")
print(f"访问类型(type):{type_used}(range表示范围扫描,高效)")
except Error as e:
print(f"索引失效验证出错:{e}")
finally:
if connection and connection.is_connected():
cursor.close()
connection.close()
if __name__ == "__main__":
index_invalidation_demo()
运行结果(规整对齐)
=== 场景1:函数操作索引字段(索引失效)===
使用索引(key):NULL(NULL表示失效)
访问类型(type):ALL(ALL表示全表扫描)
=== 场景2:不操作索引字段(索引有效)===
使用索引(key):idx_orders_order_date(非NULL表示有效)
访问类型(type):range(range表示范围扫描,高效)
8.6 数据库其他性能调优技巧
除索引外,还有多种方法可提升数据库性能,以下为 Python 开发中常用的调优手段:
8.6.1 优化查询语句
- 只查询需要的字段:避免SELECT *,减少数据传输量。
- 错误:SELECT * FROM users WHERE id = 1
- 正确:SELECT name, email FROM users WHERE id = 1
- 限制查询结果数量:使用LIMIT避免返回大量无关数据(如分页查询)。
- 示例:SELECT * FROM orders ORDER BY id DESC LIMIT 10(只返回最新 10 条订单)
- 避免嵌套过深的子查询: