在DB2数据库运维与开发过程中,索引设计直接关系到SQL的执行效率与并发写入能力。唯一索引用来保证业务数据的实体完整性,复合索引则常用于覆盖多条件查询,但两者在创建语法、存储结构以及运行期行为上都有不少容易踩坑的细节。如果仅凭经验照搬其他关系型数据库的做法,很可能在DB2上遇到意料之外的锁表、导入失败或者索引不被使用的问题。

唯一索引的创建约束与数据校验机制
DB2中的唯一索引通过CREATE UNIQUE INDEX语句建立,它要求索引键所对应的每一行取值组合在表中绝对不重复。与主键约束不同,唯一索引允许索引键列包含空值,并且一个表可以创建多个唯一索引,而主键只能有一个。在创建时如果表中已经存在重复数据,DB2会立刻抛出SQLSTATE 23505错误并终止语句,不会像某些数据库那样提供忽略选项。
这种强校验机制在批量数据迁移场景里尤其需要注意。很多团队在从旧系统导出数据后,由于清洗逻辑遗漏,导致目标表在构建唯一索引之前就已经混入重复记录。此时直接执行创建语句会让整个索引构建失败,而且在大表上DB2往往需要先排序再建树,失败后的回滚也会消耗大量日志与临时空间。推荐的做法是先使用SELECT COUNT(*)配合GROUP BY找出重复组,或者借助ALTER TABLE加约束前用IMPORT的REPLACE方式重置数据。
另一个常被忽视的点是唯一索引对NULL的处理。DB2遵循标准SQL语义,认为两个NULL互不相等,因此允许多行在唯一索引列上均为NULL。如果业务上要求“空值也不能重复”,就必须改用COALESCE生成派生列再建索引,或者在前端写入时做应用层拦截。下面示例展示如何安全地创建一个基于两列的唯一索引:
-- 在订单表上建立客户ID与订单日期的唯一索引 CREATE UNIQUE INDEX idx_cust_order ON orders (cust_id, order_date) INCLUDE (status); -- 若需排查已有重复数据 SELECT cust_id, order_date, COUNT(*) FROM orders GROUP BY cust_id, order_date HAVING COUNT(*) > 1;
复合索引的列顺序与前导列选择
复合索引又称组合索引,是将多列按声明顺序拼接成一个键树。DB2的优化器在使用复合索引时遵循“最左前缀”原则:只有查询条件中包含了前导列,索引才可能被用于匹配。如果把高基数列放在后面而低基数列放前面,例如把性别放在第一列、手机号放在第二列,那么仅用手机号查询时就无法有效利用该索引,只能做全索引扫描甚至全表扫描。
除了前导列,复合索引内部列的顺序还影响排序与覆盖能力。当查询的ORDER BY子句与索引列顺序一致时,DB2可以免去额外的排序步骤;若顺序相反或中间断列,则很可能触发排序算子。此外,从DB2 9.7开始支持的INCLUDE列可以将不需要参与查找但会被查询选中的列附加在叶子节点,从而减少回表。但要注意INCLUDE列不参与索引键比较,也不能用于范围过滤。
我们通过一个具体例子来看列顺序差异带来的执行计划变化。假设有用户表,常在后台按“城市+注册时间”筛选,偶尔按“注册时间”单独查。若索引定义为(city, reg_time),则单独按reg_time查时无法走索引;若定义为(reg_time, city),两种查询都能利用前导列reg_time。相关建表与索引脚本如下:
CREATE TABLE user_profile ( id BIGINT NOT NULL, city VARCHAR(30), reg_time TIMESTAMP, age INT ); -- 推荐:高基数的时间列作为前导列 CREATE INDEX idx_reg_city ON user_profile (reg_time, city) INCLUDE (age); -- 仅按注册时间查询,可命中索引 SELECT id, age FROM user_profile WHERE reg_time >= '2023-01-01';
创建索引过程中的锁与性能影响
在DB2中执行CREATE INDEX默认会对表加共享锁,并在构建期间阻止其他事务进行结构变更。对于线上大表,索引构建可能持续数分钟甚至更久,这段时间虽然普通读写不一定被阻塞(取决于锁升级与隔离级别),但会产生大量日志写入。若系统开启了归档并磁盘IO紧张,就可能引发应用超时。因此建议在低峰期操作,或使用DEFERRED方式先建空索引再逐步填充。
另外,唯一索引的构建比非唯一索引更耗资源,因为DB2必须在排序后逐行做重复性探测,无法采用批量装载的宽松模式。复合索引的列数越多,键长度越大,每个索引页能存放的条目越少,树的高度更容易增加,进而使查询的IO次数上升。经验上,复合索引列数不宜超过四列,且尽量把定长、短小的列放在前面以压缩键大小。
还可以利用DB2的ALLOW REVERSE SCANS与MINPCTUSED等参数调节索引行为。反向扫描能缓解右倾插入导致的页热点,而页利用率参数可减少页分裂频率。以下示例展示带参数创建的复合唯一索引,适合高并发插入且需要避免重复的场景:
CREATE UNIQUE INDEX idx_txn_no ON transaction_log (acct_id, txn_seq) ALLOW REVERSE SCANS MINPCTUSED 70; -- 查看索引状态 SELECT indname, iid, index_type, status FROM syscat.indexes WHERE tabname = 'TRANSACTION_LOG';
综合来看,DB2的唯一索引与复合索引并非简单套用语法即可高枕无忧。从数据清洗、列顺序设计到上线时的锁与日志控制,每一环都需要结合业务查询特征与表规模来权衡。只有理解B+树存储、优化器匹配规则以及DB2特有的索引属性,才能构建出既保完整又高性能的索引体系。
DB2unique_indexcomposite_index修改时间:2026-08-16 00:20:31