面试题答案
一键面试索引设计与优化
- 分析查询语句:
- 仔细研究经常执行的复杂连接查询语句,找出WHERE子句中的过滤条件、JOIN子句中的连接条件、ORDER BY子句中的排序字段以及聚合操作涉及的字段。
- 例如,若查询语句为
SELECT column1, column2, COUNT(*) FROM complex_table JOIN related_table ON complex_table.id = related_table.complex_id WHERE complex_table.status = 'active' AND related_table.category = 'category1' ORDER BY complex_table.date_created;
,则重点关注complex_table.status
、related_table.category
、连接条件complex_table.id = related_table.complex_id
以及排序字段complex_table.date_created
。
- 创建索引:
- 单列索引:对于WHERE子句中的单个过滤条件字段,如
complex_table.status
和related_table.category
,创建单列索引。在SQLite中可以使用CREATE INDEX idx_status ON complex_table(status);
和CREATE INDEX idx_category ON related_table(category);
来创建。 - 复合索引:对于连接条件和经常一起使用的过滤条件,创建复合索引。例如,对于连接条件
complex_table.id = related_table.complex_id
,可以创建复合索引CREATE INDEX idx_join ON related_table(complex_id);
。如果过滤条件和连接条件经常一起使用,如complex_table.status
和连接条件,可以创建复合索引CREATE INDEX idx_status_join ON complex_table(status, id);
。注意复合索引的字段顺序很重要,一般把选择性高(不同值多)的字段放在前面。 - 覆盖索引:当查询中涉及的字段都包含在索引中时,可以创建覆盖索引。例如,若查询为
SELECT status, date_created FROM complex_table WHERE status = 'active' ORDER BY date_created;
,可以创建覆盖索引CREATE INDEX idx_status_date ON complex_table(status, date_created);
,这样查询时可以直接从索引中获取数据,避免回表操作,提高查询性能。
- 单列索引:对于WHERE子句中的单个过滤条件字段,如
- 定期维护索引:
- 随着数据的插入、更新和删除,索引可能会变得碎片化,影响性能。可以定期使用
VACUUM
命令对SQLite数据库进行优化,它会重建数据库文件,整理索引结构,减少碎片化。
- 随着数据的插入、更新和删除,索引可能会变得碎片化,影响性能。可以定期使用
性能瓶颈及解决方案
- 索引膨胀:
- 瓶颈:过多或不合理的索引会占用大量的磁盘空间,并且在数据插入、更新和删除时,索引的维护成本增加,导致写入性能下降。
- 解决方案:定期评估索引的使用情况,删除那些很少使用或者对性能没有提升的索引。可以通过分析查询日志,查看哪些索引在实际查询中被使用,哪些从未被使用。
- 高并发写入问题:
- 瓶颈:SQLite在高并发写入场景下,会因为锁机制导致性能瓶颈。默认情况下,SQLite使用共享 - 独占锁,写入操作会独占数据库文件,其他写入和大部分读取操作需要等待锁释放。
- 解决方案:使用 WAL(Write - Ahead Logging)模式。在WAL模式下,写入操作不会独占数据库文件,而是将修改记录写入到一个单独的日志文件中,允许多个读写操作并发执行。可以通过
PRAGMA journal_mode = WAL;
来设置。
- 复杂查询优化限制:
- 瓶颈:对于极其复杂的查询,即使有合适的索引,SQLite的查询优化器可能也无法生成最优的执行计划,导致性能不佳。
- 解决方案:可以尝试使用
EXPLAIN QUERY PLAN
命令来查看查询执行计划,根据执行计划分析问题所在。有时可以通过重写查询语句,改变连接顺序、使用临时表等方式,帮助查询优化器生成更好的执行计划。例如,将复杂的多表连接查询拆分成多个步骤,使用临时表存储中间结果,再进行后续的连接和计算。