加强政务网站建设万网域名控制台
2026/5/21 12:54:35 网站建设 项目流程
加强政务网站建设,万网域名控制台,站长统计代码,静态网站培训文章目录一、前置知识#xff1a;如何判断索引是否生效#xff1f;1.1 使用 EXPLAIN (ANALYZE, BUFFERS)1.2 检查索引是否存在及类型1.3 索引失效的本质和解决思路1.4 预防索引的建议二、十大索引失效原因详解原因一#xff1a;查询条件未使用索引列#xff08;最基础错误如何判断索引是否生效1.1 使用 EXPLAIN (ANALYZE, BUFFERS)1.2 检查索引是否存在及类型1.3 索引失效的本质和解决思路1.4 预防索引的建议二、十大索引失效原因详解原因一查询条件未使用索引列最基础错误原因二对索引列使用函数或表达式原因三使用 ! 或 NOT IN 导致全表扫描原因四LIKE 查询以通配符开头原因五复合索引的列顺序不当原因六数据分布倾斜导致优化器放弃索引原因七未更新表统计信息ANALYZE 缺失原因八索引列存在大量 NULL 值且查询未处理原因九使用 OR 条件且非所有分支有索引原因十数据量过小优化器认为索引无必要三、高级诊断技巧技巧 1强制索引扫描仅用于测试技巧 2查看优化器成本参数技巧 3使用 pg_hint_plan 扩展谨慎在 PostgreSQL 中索引是提升查询性能的核心手段。然而“建了索引却未被使用”是 DBA 和开发者最常遇到的性能陷阱。索引失效不仅浪费存储和写入开销更会导致查询慢如蜗牛。本文将系统剖析PostgreSQL 索引失效的十大常见原因结合执行计划分析、原理说明和修复方案助你精准定位并解决索引未命中问题。一、前置知识如何判断索引是否生效在分析原因前需掌握验证方法1.1 使用EXPLAIN (ANALYZE, BUFFERS)EXPLAIN(ANALYZE,BUFFERS)SELECT*FROMusersWHEREemailaliceexample.com;关键观察点Index Scan/Bitmap Index Scan索引被使用Seq Scan全表扫描索引未生效Rows Removed by Filter过滤掉的行数值大说明选择性差1.2 检查索引是否存在及类型-- 查看表的所有索引\dusers-- 查看索引定义SELECTindexname,indexdefFROMpg_indexesWHEREtablenameusers;1.3 索引失效的本质和解决思路索引失效的本质是“优化器认为索引扫描的成本高于其他方式”。这可能是由于查询写法问题函数、OR、前导通配符数据分布问题倾斜、NULL、小表元数据问题统计信息过期索引设计问题列顺序、缺失解决思路应为用EXPLAIN确认是否失效对照十大原因逐项排查优先改写查询或调整设计而非强制索引1.4 预防索引的建议设计阶段根据查询模式设计索引而非盲目建索引复合索引遵循“等值列在前范围列在后”原则开发阶段避免在索引列上使用函数/表达式优先使用前缀匹配LIKE abc%运维阶段定期执行ANALYZE尤其大批量写入后监控慢查询日志及时发现未命中索引的 SQL监控阶段使用pg_stat_user_indexes查看索引使用率SELECTschemaname,tablename,indexname,idx_scanFROMpg_stat_user_indexesWHEREidx_scan0;-- 从未使用的索引删除无用索引减少写入开销二、十大索引失效原因详解原因一查询条件未使用索引列最基础错误1、现象索引建在email列但查询条件使用name。-- 索引CREATE INDEX idx_users_email ON users(email);SELECT*FROMusersWHEREnameAlice;-- 无法使用 idx_users_email2、修复为name列单独建索引或创建复合索引(name, email)若常联合查询注意索引只能加速其包含的列的查询。原因二对索引列使用函数或表达式1、现象在 WHERE 条件中对索引列进行计算、函数调用或类型转换。-- 索引CREATE INDEX idx_users_email ON users(email);-- ❌ 失效函数包裹SELECT*FROMusersWHEREUPPER(email)ALICEEXAMPLE.COM;-- ❌ 失效表达式SELECT*FROMusersWHEREemail||domain.comalicedomain.com;-- ❌ 失效隐式类型转换SELECT*FROMusersWHEREemail123;-- email 是 text123 是 integer2、原理PostgreSQL 无法将函数结果与索引树直接比对必须先计算每行的函数值。3、修复方案方案 A改写查询推荐-- 改为常量比较SELECT*FROMusersWHEREemailaliceexample.com;方案 B创建函数索引Function-Based IndexCREATEINDEXidx_users_upper_emailONusers(UPPER(email));-- 查询需完全匹配索引表达式SELECT*FROMusersWHEREUPPER(email)ALICEEXAMPLE.COM;注意函数索引需精确匹配表达式大小写、空格均敏感。原因三使用!或NOT IN导致全表扫描1、现象-- 索引CREATE INDEX idx_users_status ON users(status);-- ❌ 通常失效SELECT*FROMusersWHEREstatus!inactive;-- ❌ 可能失效尤其当 inactive 占比很小时SELECT*FROMusersWHEREstatusNOTIN(banned,deleted);2、原理!和NOT类操作的选择性难以预估若排除的值占比很小如 1%则需扫描 99% 的数据优化器认为全表扫描更高效3、修复方案方案 A改用正向条件若业务允许-- 假设只有三种状态SELECT*FROMusersWHEREstatusIN(active,pending);方案 B确保排除值占比较高若status inactive占 90%则status ! inactive仅返回 10%索引可能被使用需通过ANALYZE更新统计信息让优化器准确估算原因四LIKE查询以通配符开头1、现象-- 索引CREATE INDEX idx_users_name ON users(name);-- ❌ 失效前导通配符SELECT*FROMusersWHEREnameLIKE%Alice%;-- ✅ 有效后缀通配符SELECT*FROMusersWHEREnameLIKEAlice%;2、原理B-tree 索引基于前缀排序无法加速任意位置的子串匹配。3、修复方案方案 A使用全文检索Full-Text Search-- 创建 tsvector 列并建索引ALTERTABLEusersADDCOLUMNname_ts tsvector;UPDATEusersSETname_tsto_tsvector(english,name);CREATEINDEXidx_users_name_tsONusersUSINGGIN(name_ts);-- 查询SELECT*FROMusersWHEREname_ts to_tsquery(Alice);方案 B使用pg_trgm扩展支持任意 LIKE-- 启用扩展CREATEEXTENSIONIFNOTEXISTSpg_trgm;-- 创建 GIN 或 GiST 索引CREATEINDEXidx_users_name_trgmONusersUSINGGIN(name gin_trgm_ops);-- 查询可使用 %Alice%SELECT*FROMusersWHEREnameLIKE%Alice%;注意pg_trgm索引体积较大仅适用于高价值模糊查询。原因五复合索引的列顺序不当1、现象复合索引(a, b, c)但查询只使用b或c。-- 索引CREATE INDEX idx_orders_user_date ON orders(user_id, order_date);-- ❌ 失效跳过首列SELECT*FROMordersWHEREorder_date2026-01-01;-- ✅ 有效使用首列SELECT*FROMordersWHEREuser_id123ANDorder_date2026-01-01;2、原理PostgreSQL 的 B-tree 复合索引遵循最左前缀原则Leftmost Prefix可用于(a)、(a,b)、(a,b,c)的查询无法用于(b)、(c)、(b,c)的查询3、修复方案方案 A调整索引列顺序将高选择性或常单独查询的列放在前面例如若常查order_date则建(order_date, user_id)方案 B创建额外索引为高频单列查询单独建索引权衡写入性能 vs 查询性能原因六数据分布倾斜导致优化器放弃索引1、现象某值占比极高如 99%即使有索引查询该值仍走全表扫描。-- 假设 status active 占 99%-- 索引CREATE INDEX idx_users_status ON users(status);-- ❌ 可能失效返回大量数据SELECT*FROMusersWHEREstatusactive;2、原理索引扫描需回表Heap Fetch当返回行数接近全表时I/O 成本高于顺序扫描优化器基于统计信息估算成本选择更优方案3、验证方法-- 查看值分布SELECTstatus,COUNT(*)FROMusersGROUPBYstatus;-- 查看统计信息SELECTattname,n_distinct,most_common_vals,most_common_freqsFROMpg_statsWHEREtablenameusersANDattnamestatus;4、修复方案方案 A接受全表扫描合理行为若查询确实需返回大部分数据全表扫描反而是最优解方案 B优化查询逻辑避免查询超高频值改用分页或聚合方案 C强制使用索引不推荐-- 临时禁用 seqscan仅用于测试SETenable_seqscanoff;-- 执行查询后立即恢复RESET enable_seqscan;警告强制索引可能导致性能更差仅用于诊断。原因七未更新表统计信息ANALYZE缺失1、现象新导入大量数据后索引突然不生效。2、原理PostgreSQL 优化器依赖pg_statistic中的统计信息估算行数。若数据变更后未执行ANALYZE统计信息过期导致错误的执行计划。3、验证-- 查看上次 analyze 时间SELECTschemaname,tablename,last_analyze,n_tup_ins,n_tup_updFROMpg_stat_user_tablesWHEREtablenameorders;4、修复-- 手动更新统计信息ANALYZEorders;-- 或调整 autovacuum 参数自动触发ALTERTABLEordersSET(autovacuum_analyze_scale_factor0.05);建议大批量数据导入后显式执行ANALYZE。原因八索引列存在大量 NULL 值且查询未处理1、现象索引列允许 NULL查询条件未考虑 NULL。-- 索引CREATE INDEX idx_users_phone ON users(phone);-- ❌ 可能低效NULL 值不存于 B-tree 索引默认SELECT*FROMusersWHEREphoneISNOTNULL;2、原理PostgreSQL 的 B-tree 索引默认不存储 NULL 值若phone IS NOT NULL返回大量行优化器可能选择全表扫描3、修复方案方案 A创建包含 NULL 的索引Partial Index-- 仅索引非 NULL 值实际同默认行为CREATEINDEXidx_users_phone_notnullONusers(phone)WHEREphoneISNOTNULL;-- 查询需匹配条件SELECT*FROMusersWHEREphone123ANDphoneISNOTNULL;方案 B明确处理 NULL-- 若业务需排除 NULL显式写出SELECT*FROMusersWHEREphoneISNOTNULLANDphoneLIKE123%;注意若需查询IS NULL应单独为 NULL 建部分索引CREATEINDEXidx_users_phone_nullONusers((1))WHEREphoneISNULL;原因九使用OR条件且非所有分支有索引1、现象-- 索引CREATE INDEX idx_users_email ON users(email);-- ❌ 失效name 无索引SELECT*FROMusersWHEREemailaexample.comORnameAlice;2、原理OR条件要求所有分支均可索引扫描才能合并结果若任一分支无法使用索引优化器可能放弃全部索引3、修复方案方案 A为所有 OR 分支建索引CREATEINDEXidx_users_nameONusers(name);-- 此时 OR 查询可能使用 BitmapOr方案 B改写为 UNION推荐SELECT*FROMusersWHEREemailaexample.comUNIONSELECT*FROMusersWHEREnameAlice;优势每个子查询独立使用索引避免优化器误判。原因十数据量过小优化器认为索引无必要1、现象测试表仅 10 行数据即使有索引也走 Seq Scan。2、原理索引扫描需额外 I/O读索引页 读数据页当表很小时全表扫描的 I/O 成本低于索引扫描3、验证-- 查看表大小SELECTpg_size_pretty(pg_total_relation_size(users));4、修复无需修复这是优化器的正确决策生产环境数据量增大后索引会自动生效建议在接近生产规模的数据集上测试索引效果。三、高级诊断技巧技巧 1强制索引扫描仅用于测试SETenable_seqscanoff;EXPLAINSELECT*FROMusersWHERE...;RESET enable_seqscan;若强制后性能更差说明优化器选择正确若强制后性能更好需检查统计信息或配置技巧 2查看优化器成本参数SHOWrandom_page_cost;-- 默认 4.0SSD 建议设为 1.1SHOWcpu_tuple_cost;在 SSD 环境下降低random_page_cost可促使更多使用索引技巧 3使用pg_hint_plan扩展谨慎/* IndexScan(users idx_users_email) */SELECT*FROMusersWHEREemailaexample.com;绕过优化器强制使用索引仅用于临时救急非长久之计

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询