面试题答案
一键面试数据库索引优化
- 分析查询语句:
- 确定查询中涉及的条件字段,例如
WHERE
子句、JOIN
子句中的字段。对这些字段建立索引可以加速数据的定位。例如,如果查询是SELECT * FROM table1 JOIN table2 ON table1.id = table2.table1_id WHERE table1.status = 'active';
,那么table1.id
、table2.table1_id
和table1.status
可能适合建立索引。 - 原因:索引就像书籍的目录,能让数据库快速定位到所需数据,减少全表扫描的开销,尤其是在大表上,全表扫描代价极高。
- 确定查询中涉及的条件字段,例如
- 复合索引:
- 如果查询条件涉及多个字段,考虑创建复合索引。比如查询
SELECT * FROM users JOIN orders ON users.id = orders.user_id WHERE users.city = 'New York' AND orders.order_date > '2023 - 01 - 01';
,可以创建复合索引CREATE INDEX idx_users_orders ON users (id, city);
和CREATE INDEX idx_orders ON orders (user_id, order_date);
。 - 原因:复合索引可以在多个条件同时满足时,更高效地定位数据。但需要注意索引字段的顺序,一般把选择性高(不同值多)的字段放在前面。
- 如果查询条件涉及多个字段,考虑创建复合索引。比如查询
- 覆盖索引:
- 若查询只需要特定的几个字段,创建覆盖索引。例如
SELECT name, age FROM users WHERE city = 'London';
,创建CREATE INDEX idx_users_city_name_age ON users (city, name, age);
- 原因:覆盖索引可以直接从索引中获取查询所需的数据,无需回表操作,减少了I/O开销。
- 若查询只需要特定的几个字段,创建覆盖索引。例如
查询语句优化
- 简化查询逻辑:
- 检查是否有冗余的子查询或
JOIN
操作。比如有些子查询可以用JOIN
替代,或者一些复杂的JOIN
可以拆分成多个简单的JOIN
逐步执行。例如,一个子查询SELECT column1 FROM (SELECT * FROM table1 WHERE condition1) AS subquery WHERE condition2;
若合适可以改写为直接的JOIN
操作。 - 原因:简化查询逻辑可以减少数据库的执行复杂度,优化器能更好地生成执行计划。
- 检查是否有冗余的子查询或
- 使用
EXPLAIN
分析:- 在查询语句前加上
EXPLAIN
,例如EXPLAIN SELECT * FROM table1 JOIN table2 ON table1.id = table2.id;
,分析执行计划。查看是否使用了正确的索引,表连接顺序是否合理等。根据分析结果调整查询语句。 - 原因:
EXPLAIN
能让我们了解数据库实际如何执行查询,从而有针对性地优化。
- 在查询语句前加上
- 避免函数操作在索引字段上:
- 例如不要写
SELECT * FROM users WHERE UPPER(name) = 'JOHN';
,而应改写为SELECT * FROM users WHERE name = 'john';
(假设不区分大小写查询可以这样处理)。 - 原因:对索引字段进行函数操作会导致索引失效,使查询变成全表扫描。
- 例如不要写
Python代码层面优化
- 连接池的合理使用:
- 使用如
DBUtils
这样的连接池库。在项目初始化时创建连接池,例如:
- 使用如
from dbutils.pooled_db import PooledDB
import pymysql
pool = PooledDB(
creator=pymysql,
host='localhost',
user='user',
password='password',
database='test',
autocommit=True,
maxconnections=10
)
- 每次需要数据库连接时从连接池获取连接,使用完后归还连接。
- 原因:连接池可以减少每次创建和销毁数据库连接的开销,因为创建数据库连接是比较耗时的操作。同时合理设置连接池大小可以避免过多连接占用系统资源。
2. 批量操作:
- 如果查询涉及多次相同的操作(如多次插入数据),使用批量操作。例如使用 executemany
方法:
data = [(1, 'value1'), (2, 'value2')]
conn = pool.connection()
cursor = conn.cursor()
cursor.executemany("INSERT INTO table (id, value) VALUES (%s, %s)", data)
conn.commit()
cursor.close()
conn.close()
- 原因:批量操作减少了与数据库的交互次数,从而提高性能,同时也减少了网络开销。