Azure SQL数据库唯一键约束失效,出现重复记录如何排查?
问题背景
我们有一套运行8年以上的大型SaaS系统数据库,基于Azure SQL构建,配套Web应用也部署在Azure平台上,此前从未出现问题。今日凌晨,部分C# Web应用报表突然因检测到表中存在重复记录而失败。经核查,涉事表中确实存在违反唯一键约束的完全重复记录,请问为何唯一键在插入/更新操作时未生效?
编辑补充:涉事表结构
CREATE TABLE [tenant_clientnamehere].[tbl_cachedstock]( [clusteringkey] [bigint] IDENTITY(1,1) NOT NULL, [islivecache] [bit] NOT NULL, [id] [uniqueidentifier] NOT NULL, [stocklocation_id] [uniqueidentifier] NOT NULL, [stocklocation_referencecode] [nvarchar](50) NOT NULL, [stocklocation_description] [nvarchar](max) NOT NULL, [productreferencecode] [nvarchar](50) NOT NULL, [productdescription] [nvarchar](max) NOT NULL, [unitofmeasurename] [nvarchar](50) NOT NULL, [targetstocklevel] [decimal](12, 3) NULL, [minimumreplenishmentquantity] [decimal](12, 3) NULL, [minimumstocklevel] [decimal](12, 3) NULL, [packsize] [int] NOT NULL, [isbuffermanageddynamically] [bit] NOT NULL, [dbmcheckperioddays] [int] NULL, [dbmcheckperiodbuffergroup_id] [uniqueidentifier] NULL, [ignoredbmuntildate] [datetime2](7) NULL, [notes1] [nvarchar](100) NOT NULL, [notes2] [nvarchar](100) NOT NULL, [notes3] [nvarchar](100) NOT NULL, [notes4] [nvarchar](100) NOT NULL, [notes5] [nvarchar](100) NOT NULL, [notes6] [nvarchar](100) NOT NULL, [notes7] [nvarchar](100) NOT NULL, [notes8] [nvarchar](100) NOT NULL, [notes9] [nvarchar](100) NOT NULL, [notes10] [nvarchar](100) NOT NULL, [seasonaleventreferencecode] [nvarchar](50) NULL, [seasonaleventtargetstocklevel] [decimal](12, 3) NULL, [isarchived] [bit] NOT NULL, [isobsolete] [bit] NOT NULL, [currentstocklevel] [decimal](12, 3) NULL, [quantityenroute] [decimal](12, 3) NULL, [recommendedreplenishmentquantity] [decimal](12, 3) NULL, [bufferpenetrationpercentage] [int] NOT NULL, [bufferzone] [nvarchar](10) NOT NULL, [bufferpenetrationpercentagereplenishment] [int] NOT NULL, [bufferzonereplenishment] [nvarchar](10) NOT NULL, CONSTRAINT [PK_tbl_cachedstock] PRIMARY KEY CLUSTERED ( [clusteringkey] ASC )WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY], CONSTRAINT [UK_tbl_cachedstock_1] UNIQUE NONCLUSTERED ( [islivecache] ASC, [id] ASC )WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO ALTER TABLE [tenant_clientnamehere].[tbl_cachedstock] ADD CONSTRAINT [DF__tbl_cache__isarc__1A200257] DEFAULT ((0)) FOR [isarchived] GO ALTER TABLE [tenant_clientnamehere].[tbl_cachedstock] ADD CONSTRAINT [DF__tbl_cache__isobs__1B142690] DEFAULT ((0)) FOR [isobsolete] GO
冲突记录详情
两条违反唯一键约束的记录键值为:
islivecache = 1id = BA7AD2FD-EFAA-485C-A200-095626C583A3
可能的原因及排查方向
1. 约束被临时禁用或未恢复
检查是否有运维操作(如批量数据导入、表结构变更)临时禁用了唯一键约束UK_tbl_cachedstock_1,操作完成后未重新启用。执行以下查询确认约束状态:
SELECT name, is_disabled FROM sys.indexes WHERE object_id = OBJECT_ID('tenant_clientnamehere.tbl_cachedstock') AND name = 'UK_tbl_cachedstock_1';
2. 并发竞态条件导致重复插入
如果应用采用"先查询是否存在,再插入/更新"的拆分逻辑,且未用原子操作或事务兜底,高并发场景下可能出现两个请求同时通过存在性校验,随后插入相同键值的记录。Azure SQL默认READ COMMITTED隔离级别无法完全阻止此类竞态。
核查应用代码:
- 是否存在非原子性的插入/更新逻辑?
- 关键操作是否包裹在事务中?
- 是否未利用数据库唯一约束做最终兜底校验?
3. 唯一索引IGNORE_DUP_KEY参数变更
虽然当前表结构中IGNORE_DUP_KEY = OFF,但需确认近期是否有ALTER INDEX操作修改过该参数。若该参数曾被设为ON,插入重复键值时仅返回警告而非错误,会导致重复记录被插入。可通过Azure SQL审计日志查询索引历史变更。
4. 高可用同步异常
若系统使用Azure SQL异地复制、镜像或只读副本,同步过程中可能出现异常,导致主副本约束未正确同步到副本,或副本数据回写主副本时引入重复记录。检查Azure Portal中数据库同步状态,查看是否有失败的同步任务。
5. 批量数据操作绕过约束
近期若有bcp、BULK INSERT或INSERT...SELECT等批量操作,且使用了IGNORE_CONSTRAINTS或TABLOCK选项(部分场景下会临时禁用约束检查),可能导致重复记录插入。核查数据库导入/恢复日志,确认是否存在此类操作。
6. 数据库引擎版本bug(低概率)
Azure SQL特定版本可能存在唯一约束失效的已知bug。检查当前数据库版本,对比微软官方已知问题列表,确认是否有匹配的bug及对应补丁。
临时解决方案
- 删除重复记录,恢复报表服务:
WITH CTE AS ( SELECT *, ROW_NUMBER() OVER(PARTITION BY islivecache, id ORDER BY clusteringkey) AS RowNum FROM tenant_clientnamehere.tbl_cachedstock WHERE islivecache = 1 AND id = 'BA7AD2FD-EFAA-485C-A200-095626C583A3' ) DELETE FROM CTE WHERE RowNum > 1;
- 重新验证并重建唯一键约束:
ALTER INDEX UK_tbl_cachedstock_1 ON tenant_clientnamehere.tbl_cachedstock REBUILD;
内容的提问来源于stack exchange,提问作者DrObey

