解决方案2:使用timestamp数据类型
SQL Server有一个数据类型是timestamp,它是实现乐观锁的另一种途径,每次更新SQL Server数据时,时间戳会自动产生一个唯一的二进制数值,时间戳数据类型可用来版本化你的记录更新。

图 6 timestamp数据类型
为了实现乐观锁,首先需要取得旧的时间戳值,在更新时检查旧的时间戳值是否等于当前时间戳,如:
update tbl_items set itemname=@itemname where CurrentTimestamp=@OldTimeStamp
然后检查是否发生了更新操作,如果没有发生更新,则使用SQL Server的raiserror产生一系列错误消息。
if(@@rowcount=0)
begin
raiserror('Hello some else changed the value',16,10)
end
begin
raiserror('Hello some else changed the value',16,10)
end
如果发生了并发冲突,当你如下图所示这样调用ExecuteNonQuery时,你应该会看到错误传播。

图 7 时间戳发生变化,存储过程产生了错误
解决方案3:检查旧值和新值
许多时候,我们只需要检查相关字段值的一致性,其它字段则可以忽略,在update语句中,我们可以直接做这种比较。
update tbl_items set itemname=@itemname where itemname=@OldItemNameValue
但使用乐观锁似乎没有完全解决问题,使用乐观锁只能检查并发性问题,为了从根源上解决并发性问题,我们需要使用悲观锁,因此乐观锁能起到预防作用,而悲观锁则能治愈。
什么是悲观锁?
悲观锁总是假定会发生并发性/冲突问题,因此会先对记录上锁,然后再更新。

图 8 悲观锁
如何处理悲观锁?
我们可以在SQL Server存储过程中指定IsolationLevel(隔离级别),ADO.NET级别或使用事务范围对象处理悲观锁。
使用悲观锁可以获得什么样的锁?
使用悲观锁可以获得四种类型的锁:共享(Shared)、独占(Exclusive),更新(Update)和意图(Intent),前两个是真实的锁,后面两个是锁和标记的混合。
| 何时使用 | 允许读 | 允许写 | |
| 共享锁 | 当你只想读,不希望其它事务进行更新时 | 是 | 否 |
| 独占锁 | 当你想修改数据,同时不希望别人可以读,直到你更新完毕时 | 否 | 否 |
| 更新锁 | 这是一个混合锁,当你执行的更新操作有多个步骤时使用 | ||
| 读阶段 | 是 | 否 | |
| 操作阶段 | 是 | 否 | |
| 更新阶段 | 否 | 否 | |
| 意向锁(请求锁) | 意向锁是分级的,当你想锁定下级资源时使用,例如,在表上的一个共享意向锁意味着共享锁是针对页面和表中的行的, | 不适用 | 不适用 |
| 模式锁 | 当你修改表结构时使用 | 否 | 否 |
| 大数据块更新锁 | 当你执行大数据块更新时使用 | 表级(否) | 表级(否) |