mysql 낙관적 잠금은 비관적 잠금과 비교됩니다. 낙관적 잠금은 일반적인 상황에서는 데이터가 충돌을 일으키지 않는다고 가정합니다. 따라서 업데이트를 위해 데이터를 제출하면 충돌이 발견되면 공식적으로 감지됩니다. 그런 다음 사용자에게 오류 정보를 반환하고 사용자가 무엇을 할지 결정하도록 합니다.
mysql 낙관적 잠금은 (Optimistic Locking) 비관적 잠금과 비교하여 낙관적 잠금은 정상적인 상황에서는 데이터가 충돌을 일으키지 않을 것이라고 가정하므로 데이터가 제출되고 업데이트될 때 데이터가 공식적으로 업데이트됩니다. 충돌이 있는지 확인합니다. 충돌이 발견되면 오류 정보가 사용자에게 반환되고 사용자는 무엇을 할지 결정할 수 있습니다.
그러면 낙관적 잠금을 어떻게 구현합니까? 일반적으로 두 가지 방법이 있습니다.
1 데이터 버전(버전) 기록 메커니즘을 사용하여 가장 일반적으로 사용되는 낙관적 잠금 구현 방법입니다. 데이터 버전이란 무엇입니까? 이는 일반적으로 데이터베이스 테이블에 숫자 "버전" 필드를 추가하여 데이터에 버전 식별자를 추가하는 것입니다. 데이터를 읽을 때 버전 필드의 값을 함께 읽어 데이터가 업데이트될 때마다 버전 값이 1씩 증가합니다.
업데이트를 제출할 때 데이터베이스 테이블의 해당 레코드의 현재 버전 정보를 처음 가져온 버전 값과 비교합니다. 데이터베이스 테이블의 현재 버전 번호가 가져온 버전 값과 같습니다. 처음으로 업데이트되면 업데이트되고, 그렇지 않으면 만료된 데이터로 간주됩니다. 다음 그림을 사용하여 설명합니다.
위 그림과 같이 업데이트 작업이 순차적으로 수행되면 데이터 버전이 충돌 없이 순차적으로 증가합니다. 그러나 서로 다른 비즈니스 운영이 동일한 버전의 데이터를 수정하는 경우 먼저 제출된 작업(그림의 B)은 데이터 버전을 2로 업데이트합니다. A가 B 이후에 업데이트를 제출하면 데이터 버전이 수정된 것으로 확인됩니다. . 이면 A의 업데이트 작업이 실패합니다.
2. 낙관적 잠금의 두 번째 구현 방법은 첫 번째와 유사합니다. 또한 낙관적 잠금 제어가 필요한 필드를 추가합니다. 필드 유형은 유사합니다. 위 버전에서는 업데이트가 제출되면 현재 데이터베이스에 있는 데이터의 타임스탬프를 확인하고 업데이트 전에 얻은 타임스탬프와 비교합니다. 일치하면 괜찮습니다. 그렇지 않으면 버전 충돌입니다.
관련 학습 권장 사항:
사용 예:
MySQL InnoDB
를 예로 들어MySQL InnoDB
为例
还是拿之前的实例来举:商品goods表中有一个字段status,status为1代表商品未被下单,status为2代表商品已经被下单,那么我们对某个商品下单时必须确保该商品status为1。假设商品的id为1。
下单操作包括3步骤:
1、查询出商品信息
select (status,status,version) from t_goods where id=#{id}
2、根据商品信息生成订单
3、修改商品status为2
update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};
那么为了使用乐观锁,我们首先修改t_goods表,增加一个version字段,数据默认version值为1。
t_goods
表初始数据如下:
mysql> select * from t_goods; +----+--------+------+---------+ | id | status | name | version | +----+--------+------+---------+ | 1 | 1 | 道具 | 1 | | 2 | 2 | 装备 | 2 | +----+--------+------+---------+ 2 rows in set mysql>
对于乐观锁的实现,我使用MyBatis来进行实践,具体如下:
Goods实体类:
/** * ClassName: Goods <br/> * Function: 商品实体. <br/> * date: 2013-5-8 上午09:16:19 <br/> * @author chenzhou1025@126.com */ public class Goods implements Serializable { /** * serialVersionUID:序列化ID. */ private static final long serialVersionUID = 6803791908148880587L; /** * id:主键id. */ private int id; /** * status:商品状态:1未下单、2已下单. */ private int status; /** * name:商品名称. */ private String name; /** * version:商品数据版本号. */ private int version; @Override public String toString(){ return "good id:"+id+",goods status:"+status+",goods name:"+name+",goods version:"+version; } //setter and getter }
GoodsDao
/** * updateGoodsUseCAS:使用CAS(Compare and set)更新商品信息. <br/> * * @author chenzhou1025@126.com * @param goods 商品对象 * @return 影响的行数 */ int updateGoodsUseCAS(Goods goods);
mapper.xml
<update id="updateGoodsUseCAS" parameterType="Goods"> <![CDATA[ update t_goods set status=#{status},name=#{name},version=version+1 where id=#{id} and version=#{version} ]]> </update>
GoodsDaoTest测试类
@Test public void goodsDaoTest(){ int goodsId = 1; //根据相同的id查询出商品信息,赋给2个对象 Goods goods1 = this.goodsDao.getGoodsById(goodsId); Goods goods2 = this.goodsDao.getGoodsById(goodsId); //打印当前商品信息 System.out.println(goods1); System.out.println(goods2); //更新商品信息1 goods1.setStatus(2);//修改status为2 int updateResult1 = this.goodsDao.updateGoodsUseCAS(goods1); System.out.println("修改商品信息1"+(updateResult1==1?"成功":"失败")); //更新商品信息2 goods1.setStatus(2);//修改status为2 int updateResult2 = this.goodsDao.updateGoodsUseCAS(goods1); System.out.println("修改商品信息2"+(updateResult2==1?"成功":"失败")); }
输出结果:
good id:1,goods status:1,goods name:道具,goods version:1 good id:1,goods status:1,goods name:道具,goods version:1 修改商品信息1成功 修改商品信息2失败
说明:
在GoodsDaoTest
测试方法中,我们同时查出同一个版本的数据,赋给不同的goods对象,然后先修改good1对象然后执行更新操作,执行成功。然后我们修改goods2,执行更新操作时提示操作失败。此时t_goods
이전 예를 들어보겠습니다. 상품에 제품이 있습니다. 테이블 필드 상태에서 상태 1은 제품이 주문되지 않았음을 의미하고, 상태 2는 제품이 주문되었음을 의미합니다. 그러면 특정 제품을 주문할 때 제품의 상태를 확인해야 합니다. 1입니다. 제품 ID가 1이라고 가정합니다.
mysql> select * from t_goods; +----+--------+------+---------+ | id | status | name | version | +----+--------+------+---------+ | 1 | 2 | 道具 | 2 | | 2 | 2 | 装备 | 2 | +----+--------+------+---------+ 2 rows in set mysql>
update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};
t_goods
테이블의 초기 데이터는 다음과 같습니다. 🎜rrreee🎜낙관적 잠금 구현을 위해 MyBatis를 사용하여 연습합니다. 🎜🎜🎜Goods 엔터티 클래스: 🎜🎜rrreee🎜 🎜GoodsDao🎜🎜rrreee 🎜🎜mapper.xml🎜🎜rrreee🎜🎜GoodsDaoTest 테스트 클래스 🎜🎜rrreee🎜출력 결과: 🎜rrreee🎜🎜설명: 🎜🎜🎜GoodsDaoTest 코드> 테스트 방법에서 우리는 동일한 버전과 동시에 다른 상품 개체에 데이터를 할당한 후 good1 개체를 먼저 수정한 후 업데이트 작업을 수행하여 실행이 성공합니다. 그런 다음 good2를 수정하고 업데이트 작업을 수행할 때 작업이 실패했다는 메시지가 표시됩니다. 이때 <code>t_goods
테이블의 데이터는 다음과 같습니다. 🎜rrreee🎜 첫 번째 업데이트에서 ID가 1인 데이터 버전이 2로 수정된 것을 확인할 수 있습니다. 따라서 good2를 업데이트하면 조건이 더 이상 일치하지 않으므로 업데이트가 성공하지 못합니다. 구체적인 SQL은 다음과 같습니다. 🎜rrreee🎜이 방법으로 낙관적 잠금을 달성했습니다. 🎜위 내용은 mysql 낙관적 잠금이란 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!