一、总体原则
清晰分工
复杂业务拆分为「主存储过程 + 子存储过程」模式,主过程控制流程,子过程各司其职。职责单一
每个存储过程只处理单一业务逻辑,避免流程交叉、职责混杂。可测试性与可追踪性
必须支持独立调试和链路跟踪(Trace ID传递)。性能优先
保证索引友好
尽量批量处理,减少循环
合理控制事务范围
避免锁表、长事务
安全性
防止SQL注入
避免非必要动态SQL
权限最小化设计
二、存储过程分类与命名规范
类型 | 命名规则 | 示例 |
---|---|---|
主流程存储过程 | sp_<模块名>_<功能名>_main | sp_order_create_main |
子流程存储过程 | sp_<模块名>_<功能名>sub<子功能名> | sp_order_create_sub_discount |
功能性函数 | fn_<功能描述> | fn_calc_total_price |
触发器 | trg_<表名>_<动作> | trg_user_update |
视图 | view_<模块名>_<用途描述> | view_order_summary |
临时表 | tmp_<表名>_<用途描述> | tmp_order_processing |
日志表 | log_<模块名>_<用途描述> | log_proc_error |
错误码表 | error_code_<模块名> | error_code_system |
命名规范统一要求
全部小写,单词间使用下划线
_
分隔。不使用驼峰(camelCase)或帕斯卡(PascalCase)。
禁止中文、拼音。
三、存储过程结构标准
1. 头部注释(必需)
/*
* 名称: sp_order_create_main
* 类型: 主流程存储过程
* 作者: 张三 (zhangsan@example.com)
* 创建时间: 2025-04-28
* 版本: 1.0.0
* 说明: 创建订单并初始化状态
* 更新记录:
* - 2025-05-01 张三: 增加库存扣减子过程调用
*/
2. 输入输出参数规范
所有参数统一小写+蛇形命名。
输入参数必须标明数据类型、必要时标注默认值。
出参必须包括:
@code
(状态码,0表示成功,非0为失败)@msg
(错误或成功信息)@trace_id
(链路追踪标识)
示例:
CREATE PROCEDURE sp_order_create_main
@user_id BIGINT,
@order_amount DECIMAL(18,2),
@trace_id UNIQUEIDENTIFIER,
@code INT OUTPUT,
@msg NVARCHAR(500) OUTPUT
AS
BEGIN
-- 过程逻辑
END
四、事务与异常控制规范
1. 事务管理
层次 | 控制事务 | 记录日志 | 返回状态码 | Trace ID传递 |
---|---|---|---|---|
主过程 Main | ✅ | ✅ | ✅ | ✅ |
子事务 SubTx(需要局部事务) | ✅(局部控制) | ✅ | ✅ | ✅ |
功能操作 Func(计算/查询) | ❌ | 可选 | 可选 | ✅ |
主过程负责全局事务(开启、提交、回滚)。
子过程如需局部事务,需在子过程内自处理,且必须回滚或明确提交。
出现异常时,不得自动回滚主事务,只反馈状态。
2. 异常与日志记录
主过程统一捕获异常,记录到日志表。
子过程返回错误码与消息,不得直接打印日志。
标准日志表设计:
CREATE TABLE dbo.log_proc_error (
id BIGINT IDENTITY(1,1) PRIMARY KEY,
trace_id UNIQUEIDENTIFIER NOT NULL,
proc_name NVARCHAR(200) NOT NULL,
error_code INT NOT NULL,
error_msg NVARCHAR(2000) NOT NULL,
stack_trace NVARCHAR(MAX) NULL,
input_params NVARCHAR(MAX) NULL,
extra_info NVARCHAR(MAX) NULL,
create_time DATETIME2(3) DEFAULT SYSDATETIME()
);
五、编码规范
不允许使用
SELECT *
,必须明确列字段。所有SQL语句应使用合理缩进和必要注释。
重要逻辑必须分段注释,例如:
-- Step 1: 校验用户有效性
-- Step 2: 创建订单基础信息
-- Step 3: 调用库存扣减子过程
临时表统一加前缀,例如
tmp_
,中间备份表加bak_
前缀。所有数据库表字段、存储过程、视图必须添加注释(COMMENT EXTENDED属性或文档备注)。
六、Trace ID 规范
所有存储过程必须传递
@trace_id
。每一级过程调用时必须携带
@trace_id
,用于链路追踪和异常排查。
七、版本管理与变更记录
存储过程代码统一纳入Git版本管理。
每次提交时必须填写:
变更说明
时间
责任人
示例 Commit Message:
feat(sp_order_create_main): 支持优惠券扣减流程,by 张三 2025-05-01
八、常用参数与变量约定表
名称 | 含义 |
---|---|
@trace_id | 链路追踪ID |
@code | 状态码 |
@msg | 状态信息 |
@start_time / @end_time | 记录处理时间点 |
@elapsed_ms | 总耗时(毫秒) |
@error_code | 异常时的错误码 |
@error_msg | 异常时的错误描述 |
理解了。
在您提供的基础上,我将继续用正式、专业的语气,完善《SQL Server 存储过程开发规范》中缺少的锁控制部分,并保持与原规范一致的风格。
以下是新增的锁控制规范章节(可直接合并到您的文档中):
九、锁控制规范
在存储过程开发中,合理控制锁粒度与锁时长至关重要,直接关系到系统并发性与稳定性。必须遵循以下锁控制规范:
9.1 总体原则
锁粒度最小化:尽量锁定必要的数据行或数据页,避免全表锁。
锁时间最短化:尽可能缩短事务范围,减少锁持有时间。
避免隐式锁升级:通过合理索引与分页,避免系统将行锁自动升级为表锁。
必要时显式加锁:遇到高并发敏感区域,需明确指定锁策略,防止死锁和脏读。
9.2 常用锁控制方式
方法 | 说明 | 示例 |
---|---|---|
WITH (ROWLOCK) | 强制使用行锁,减少锁冲突 | SELECT * FROM table WITH (ROWLOCK) WHERE id = @id |
WITH (UPDLOCK) | 在读取时加意向更新锁,避免并发更新冲突 | SELECT * FROM table WITH (UPDLOCK) WHERE id = @id |
WITH (XLOCK) | 独占锁,读写期间禁止其他并发访问(仅限极端必要情况) | SELECT * FROM table WITH (XLOCK) WHERE id = @id |
WITH (READPAST) | 跳过已被锁定的行,适合队列式读取 | SELECT TOP 1 * FROM table WITH (READPAST) WHERE status = 'pending' |
SET TRANSACTION ISOLATION LEVEL | 控制事务隔离级别,平衡一致性与并发性 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED |
9.3 事务与锁的结合规范
事务范围要小,锁范围要窄:只在需要保持一致性的数据处理区段内部开启事务。
避免长事务:禁止在事务内进行复杂计算、长时间等待或调用外部服务。
必要时分段处理:对于批量更新、删除,应分批操作,每批控制在一定数量(如1000行以内)。
显式捕获死锁:使用 TRY...CATCH 块,在死锁出现时正确回滚并记录日志。
示例:
BEGIN TRY
BEGIN TRANSACTION;
-- 加锁读取
SELECT * FROM order_detail WITH (UPDLOCK, ROWLOCK) WHERE order_id = @order_id;
-- 更新逻辑
UPDATE order_detail
SET status = 'processed'
WHERE order_id = @order_id;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
-- 错误日志处理
EXEC sp_log_error @trace_id = @trace_id, @error_code = ERROR_NUMBER(), @error_msg = ERROR_MESSAGE();
END CATCH
9.4 特别说明
批量操作优先分页:例如大表更新,应采用
TOP(n)
分批更新策略,防止一次性锁表。避免锁等待链:严禁在持锁状态下调用其他子存储过程,防止锁顺序反转导致死锁。
读一致性策略:只读操作,如非强一致性要求,应使用
READ COMMITTED SNAPSHOT
隔离级别,以减少锁竞争。
总结
锁控制是高并发数据库系统中不可忽视的一环。规范、审慎地使用锁,才能在保证正确性的同时,最大化系统吞吐量,避免死锁、锁表等严重问题。
在高负载系统中,应专门设立锁审计监控,及时发现异常锁持有与锁等待情况,持续优化存储过程性能
十、死锁检测与日志结构建议
10.1 死锁检测
统一死锁处理机制:所有可能引发死锁的存储过程,必须使用
TRY...CATCH
块进行异常捕获。错误码识别:SQL Server 死锁错误码为 1205,应在
CATCH
块中专门识别并记录。失败重试机制(可选):对于幂等操作,死锁后允许在短时间内自动重试1-3次,避免单点失败。
示例:
BEGIN TRY
BEGIN TRANSACTION;
-- 业务操作
UPDATE product_stock WITH (ROWLOCK, UPDLOCK)
SET stock_qty = stock_qty - @qty
WHERE product_id = @product_id;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
DECLARE @error_code INT = ERROR_NUMBER();
DECLARE @error_msg NVARCHAR(4000) = ERROR_MESSAGE();
-- 针对死锁错误,单独记录
IF @error_code = 1205
BEGIN
EXEC sp_log_deadlock @trace_id = @trace_id, @error_message = @error_msg;
END
ELSE
BEGIN
EXEC sp_log_error @trace_id = @trace_id, @error_code = @error_code, @error_msg = @error_msg;
END
END CATCH
10.2 死锁日志结构建议
为后续问题定位与优化提供依据,死锁日志表推荐结构如下:
字段 | 类型 | 说明 |
---|---|---|
id | BIGINT IDENTITY | 主键,自增长 |
trace_id | NVARCHAR(100) | 调用链追踪ID |
occur_time | DATETIME | 死锁发生时间 |
proc_name | NVARCHAR(200) | 存储过程名称 |
error_message | NVARCHAR(MAX) | 错误详细信息 |
sql_text | NVARCHAR(MAX) | 当前执行SQL(可选) |
input_params | NVARCHAR(MAX) | 输入参数(可选) |
关键设计要点:
必须记录trace_id,以支持跨服务链路追踪。
出错SQL或输入参数可以选择性截取,避免日志表无限膨胀。
必须定期归档死锁日志,如按月分区或搬迁。
十一、大规模批量更新的最佳实践
在处理百万级以上数据时,禁止直接大批量更新,必须分批、分段控制,降低锁竞争与事务压力。
11.1 分批更新策略
每次操作限定**TOP(N)**条记录,如1000或5000条。
更新成功后提交,避免长事务锁表。
避免分页偏移(OFFSET),优先通过游标或ID范围递增处理。
示例:
DECLARE @batch_size INT = 1000;
DECLARE @affected_rows INT;
SET @affected_rows = 1;
WHILE (@affected_rows > 0)
BEGIN
BEGIN TRANSACTION;
UPDATE TOP (@batch_size) order_detail
SET status = 'completed'
WHERE status = 'pending';
SET @affected_rows = @@ROWCOUNT;
COMMIT TRANSACTION;
END
11.2 批处理注意事项
保持稳定的批量大小,防止批次太小浪费资源,批次太大导致锁表。
结合时间窗口更新,如夜间低峰期批量处理,避免高峰期阻塞业务。
可配参数化,批量数量、超时时间可通过存储过程参数传入,提升灵活性。
示例:批量更新支持动态传入 batch_size
CREATE PROCEDURE proc_batch_update_order
@batch_size INT = 1000
AS
BEGIN
SET NOCOUNT ON;
DECLARE @affected_rows INT = 1;
WHILE (@affected_rows > 0)
BEGIN
BEGIN TRANSACTION;
UPDATE TOP (@batch_size) order_detail
SET status = 'completed'
WHERE status = 'pending';
SET @affected_rows = @@ROWCOUNT;
COMMIT TRANSACTION;
END
END
小结
死锁处理,核心在于统一异常捕获、分类日志记录、可选重试保护。
大规模更新,核心在于控制事务体积、快速提交、分批处理。
这样既能保障数据一致性,又能最大化提升高并发场景下的吞吐量与稳定性。