## 锁机制

---


在如今分布式、高并发、各种负载纵横天下的时代，支持高访问量成为检验一个系统合不合格的重要标准，然而我们除了在运算过程中要求系统更加效率外，在最终的数据存储过程中也希望其能够准确。

**针对如何解决多线程并发产生的脏数据问题，本文简单列举一些常见案例及应对措施。
**
#### 案例一：

本地起10个线程，分别执行10次，对数据库的一条记录的sum字段（初始值为0）+1操作，中间的业务逻辑我们忽略掉，如何保证执行完毕后sum的值为100？


表结构：

|字段名|	字段类型	|可空|	字段描述	| 使用备注|
| --- | --- | --- | --- |---|
|ID	|BIGINT(20)|	N	|主键ID|	无业务含义|
|SUM|	NUMBER(20)|N	|金额	|初始值为0|

**解决措施：**

乐观锁机制，利用数据库自身的事务来解决问题，update 表 set sum=sum+#increment#   where id=#id#，适用于一些只更新数量、金额的场景。
尽量不要采用在后台计算一个最终的sum值，然后通过 update 表 set sum=#sum#  where id=#id#，因为此时在读与写的时间间隔里，很有可能其它的线程已经读过或操作过

#### 案例二：

买家操作一笔订单，执行确认收货，假如同一笔订单打开了两个窗口，开始时在一个窗口确认成功，后来在另一个窗口又点了一次，此时应如何解决？

**解决措施：**

在执行“买家确认收货”操作时，我们通常会首先查出这笔订单，判断当前操作用户是否有执行权限，同时判断当前订单的状态是否是“等待买家确认收货”，。。。，如果满足这些前置条件，才允许后面的业务操作，更新数据库。
当然，存在另一种可能，如果是通过自动化脚本操作呢？两次操作几乎同时执行，也就是说，两次的前置校验都能顺利通过(因此那时，数据库记录还没来的及更新)，此时一个好的解决方案，操作时增加前置条件，比如确认收货的前置条件是“等待买家确认收货”，如果此时订单的状态变成了成功就无法操作。
update 订单表 set  status="交易成功"  where id=#orderId# and status="等待买家确认收货"
这样，第二次操作sq条件不满足，也就避免执行两次买家确认收货操作。

#### 案例三：

增加前置条件是一个不错的解决方案，但是，不是每个业务都有前置条件，或者说前置条件不明确、无规则，此时应如何解决？


|字段名|	字段类型	|可空|	字段描述	| 使用备注|
| --- | --- | --- | --- |---|
|ID|	BIGINT(20)|	N	|主键ID|	无业务含义|
|SUM|	NUMBER(20)|	N	|金额| 初始值为0|
|attribute_cc|	INT(11)	| N	|用于为attribute加锁	|
 
**解决措施：**

可以借助memcache用到的一种同步机制（CAS），比较并交换，在数据库表增加一个冗余字段，每次操作都会自动+1。执行业务时，首先会从数据库读取该字段信息，更新业务数据时，会自动比较attribute_cc的值是否有变化，如果有变化，表示刚才读的信息已变化过，需要重新操作。