柏虎资源网

专注编程学习,Python、Java、C++ 教程、案例及资源

Python数据库编程教程:第 8 章 数据库索引优化与性能调优

在数据库数据量增长到一定规模(如万级、十万级)后,未优化的查询会变得缓慢(如查询 “某类商品的所有订单” 需全表扫描)。本章基于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 优化查询语句

  1. 只查询需要的字段:避免SELECT *,减少数据传输量。
    • 错误:SELECT * FROM users WHERE id = 1
    • 正确:SELECT name, email FROM users WHERE id = 1
  1. 限制查询结果数量:使用LIMIT避免返回大量无关数据(如分页查询)。
    • 示例:SELECT * FROM orders ORDER BY id DESC LIMIT 10(只返回最新 10 条订单)
  1. 避免嵌套过深的子查询

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言