如何在多用户并发访问数据库时确保数据的一致性和完整性?
- 内容介绍
- 文章标签
- 相关推荐
多使用者并发访问数据库时的主要痛点
在高并发环境下多使用者同时操作同一条记录时可能会遇到以下问题:
- 脏读事务A读取了事务B未提交的数据,导致数据不一致
- 幻读事务A两次查询同一范围的数据,发现有新增或删除的行
- 不可重复读事务A两次读取同一行数据,发现值已经被修改
- 更新丢失两个事务同时更新同一行数据,后一个更新覆盖前一个更新
- 锁争用死锁多个事务互相持有对方需要的锁,导致程序无法继续执行
1. 你提出的LOCKED字段方案存在的潜在问题
虽然加LOCKED字段是一种常见思路。但实际使用中可能遇到以下挑战:
- 竞态条件风险高:
-- 使用者A执行存储过程时LOCKED=0 -- 使用者B也执行存储过程时LOCKED=0 -- 使用者A将LOCKED加到1并返回true -- 使用者B也将LOCKED加到1并返回true //此时两个使用者都认为自己获得了锁 //后续修改会发生冲突
2. 数据库内置机制 - 常用方法方案比较
| 方法 | 适用场景 | 优势 | 劣势/注意事项 |
|---|---|---|---|
| 显式锁定 | - 需要绝对保证单条记录修改排他性 - 对特定记录做敏感操作 | - 完全防止并发修改冲突 - 语义清晰易理解 - 支持多表、多行锁定 | - 性能开销较大 - 需要手动处理死锁检测 - 不适合长时间占用资源操作 |
| MVCC | - 高并发场景下读写分离需求 - 需要提供历史版本查询功能 | - 高度并发友好 - 自动处理简单冲突场景 - 提供快照隔离能力 | - 写入性能有所降低 - 不能完全替代排他锁特殊场景 - 增加存储开销 |
| 悲观/乐观并存策略 | - 混合业务场景需求更综合考虑 - 对部分资源要求严格排他性操作但多数为只读操作场景 | - 平衡了安全性与性能之间关系 - 能根据实际业务情况灵活调整策略选择哪种更适合当前环境使用情况来决定使用哪种模式更优秀。不过, | 需要精确评估各类业务场景占比和特征来进行针对性部署策略组合方式使用才可达到最优效果。老实说,需要维护人员具备足够经验判断何时该切换模式以满足不同阶段需求变化。 |
推荐执行方法组合建议:
- 主要金融类交易采用SELECT FOR UPDATE+显式错误处理流程设计;
- 内容类网站信息修改可选MVCC+可重复读隔离级别;
- 混合型程序建议配置悲观乐观自适应算法框架;不过,
- 任何情况下均应配备完善的监控告警和补偿机制.
具体实现示例代码片段参考:
sql -- MySQL显式锁定示例 START TRANSACTION;说起来,SELECT * FROM accounts WHERE id = 1 FOR UPDATE;UPDATE accounts SET balance = balance - 10 WHERE id = 1; COMMIT,
-- PostgreSQL MVCC示例 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;BEGIN,SELECT * FROM products WHERE sku = 'A娱乐';UPDATE products SET inventory = inventory - 1 WHERE sku = 'A娱乐';COMMIT,
-- Oracle乐观控制示例 BEGIN TRANSACTION;SELECT version,price FROM products WHERE sku = 'XYZ' WITH;UPDATE products SET price = :newprice,version = version + 1 WHERE sku = 'XYZ' AND version = :CURRENTVERSION IF @@ROWCOUNT = 0 ROLLBACK ELSE COMMIT;
常见误区及规避方法:
-
>直接使用read="";font-weight="" <="" bold="" class="bold_red" color:red="" committed隔离级别而未考虑幻象读风险="" strong="">
>应该根据业务需求选择至少REPEATABLE READ级别或显式处理幻象情况 .
多使用者并发访问数据库时的主要痛点
在高并发环境下多使用者同时操作同一条记录时可能会遇到以下问题:
- 脏读事务A读取了事务B未提交的数据,导致数据不一致
- 幻读事务A两次查询同一范围的数据,发现有新增或删除的行
- 不可重复读事务A两次读取同一行数据,发现值已经被修改
- 更新丢失两个事务同时更新同一行数据,后一个更新覆盖前一个更新
- 锁争用死锁多个事务互相持有对方需要的锁,导致程序无法继续执行
1. 你提出的LOCKED字段方案存在的潜在问题
虽然加LOCKED字段是一种常见思路。但实际使用中可能遇到以下挑战:
- 竞态条件风险高:
-- 使用者A执行存储过程时LOCKED=0 -- 使用者B也执行存储过程时LOCKED=0 -- 使用者A将LOCKED加到1并返回true -- 使用者B也将LOCKED加到1并返回true //此时两个使用者都认为自己获得了锁 //后续修改会发生冲突
2. 数据库内置机制 - 常用方法方案比较
| 方法 | 适用场景 | 优势 | 劣势/注意事项 |
|---|---|---|---|
| 显式锁定 | - 需要绝对保证单条记录修改排他性 - 对特定记录做敏感操作 | - 完全防止并发修改冲突 - 语义清晰易理解 - 支持多表、多行锁定 | - 性能开销较大 - 需要手动处理死锁检测 - 不适合长时间占用资源操作 |
| MVCC | - 高并发场景下读写分离需求 - 需要提供历史版本查询功能 | - 高度并发友好 - 自动处理简单冲突场景 - 提供快照隔离能力 | - 写入性能有所降低 - 不能完全替代排他锁特殊场景 - 增加存储开销 |
| 悲观/乐观并存策略 | - 混合业务场景需求更综合考虑 - 对部分资源要求严格排他性操作但多数为只读操作场景 | - 平衡了安全性与性能之间关系 - 能根据实际业务情况灵活调整策略选择哪种更适合当前环境使用情况来决定使用哪种模式更优秀。不过, | 需要精确评估各类业务场景占比和特征来进行针对性部署策略组合方式使用才可达到最优效果。老实说,需要维护人员具备足够经验判断何时该切换模式以满足不同阶段需求变化。 |
推荐执行方法组合建议:
- 主要金融类交易采用SELECT FOR UPDATE+显式错误处理流程设计;
- 内容类网站信息修改可选MVCC+可重复读隔离级别;
- 混合型程序建议配置悲观乐观自适应算法框架;不过,
- 任何情况下均应配备完善的监控告警和补偿机制.
具体实现示例代码片段参考:
sql -- MySQL显式锁定示例 START TRANSACTION;说起来,SELECT * FROM accounts WHERE id = 1 FOR UPDATE;UPDATE accounts SET balance = balance - 10 WHERE id = 1; COMMIT,
-- PostgreSQL MVCC示例 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;BEGIN,SELECT * FROM products WHERE sku = 'A娱乐';UPDATE products SET inventory = inventory - 1 WHERE sku = 'A娱乐';COMMIT,
-- Oracle乐观控制示例 BEGIN TRANSACTION;SELECT version,price FROM products WHERE sku = 'XYZ' WITH;UPDATE products SET price = :newprice,version = version + 1 WHERE sku = 'XYZ' AND version = :CURRENTVERSION IF @@ROWCOUNT = 0 ROLLBACK ELSE COMMIT;
常见误区及规避方法:
-
>直接使用read="";font-weight="" <="" bold="" class="bold_red" color:red="" committed隔离级别而未考虑幻象读风险="" strong="">
>应该根据业务需求选择至少REPEATABLE READ级别或显式处理幻象情况 .

