引言
- 数据库读写看似简单,其实蕴含着许多值得思考的问题;
- 并发读写是令数据库设计者头疼的问题,其实,这个问题不仅令数据库设计者困惑,对于数据库的使用者来说,如何从业务层面高效处理并发读写问题也是不小的挑战;
- 本文就此问题做简单探讨;
问题描述
- 在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突,具体为:
- 丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新;
- 脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是6,用户B把值改为2,用户A读到的值仍为6;
数据库层面的并发控制-悲观锁
- 说明:
- 假定本次读写一定会发生并发冲突,屏蔽一切可能违反数据完整性的操作;
- 适用于实际业务中冲突发生可能性较大的情况,比如很有可能不同的人同时修改同一条数据库记录;
- 如果实际业务中冲突发生可能性较小,则用悲观锁效率很低(因为上锁的代价大);
- 使用:
- 操作之前先上锁;
- 需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX(排它表锁) 此选项被选中时,SQL Server将在整个表上置排它锁直至该命令或事务结束,这将防止其他进程读取或修改表中的数据;
数据库层面的并发控制-乐观锁
- 说明:
- 假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性,乐观锁不能解决脏读的问题;
- 适用于实际业务中冲突发生可能性较小的情况,比如几乎不可能不同的人同时修改同一条数据库记录;
- 使用:
- 数据表中专门有一个version字段(int型)记录版本信息;
- 每当这条记录改动时,这条记录对应的version字段增1;
- 每次更新时检查版本号是否一致,比如数据库中数据版本为6,更新提交时version=6+1,使用该version值(=7)与数据库version+1(=7)作比较,如果相等,则可以更新,如果不等则有可能其他程序已更新该记录,所以返回错误;
结论
- 在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;
- 但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法;
- 上面介绍的锁都是数据库层面的锁,其实,如果从业务层面来看,上面的并发控制机制还是不够的,在数据库设计时,可以设计lock字段,如果要暂时锁定某些行,令lock=1时视为锁定,业务锁面向业务,更加可靠;