一、并发控制

       同一时刻可能会有多个用户更新同一张表的同一条记录。这就会产生冲突,这就是并发性问题。数据库的并发操作通常会导致、丢失更新、读脏数据、不可重复读等问题。

二、悲观锁和乐观锁

       乐观锁顾名思义就是非常乐观啦,它认为所有的操作都不会产生并发冲突。与之相对应的就是悲观锁。它呢比较悲观,认为所有的操作都会产生并发冲突。

       其实呢,乐观锁和悲观锁在代码层(java)、缓存层(redis)、数据库层(mysql)都有自己的实现。今天呢,我们就谈一谈在数据库层的实现。

       乐观锁呢,其实并没有锁,只是这么一种叫法而已。大多是基于数据版本(Version)记录机制实现。悲观锁大多数情况下依赖数据库自身锁机制实现(咳咳,数据库自身的锁机制大概就是排他锁、共享锁了),以保证操作最大程度的独占性。加锁当然伴随着的就是资源的消耗了,比如获得获得锁,释放锁等。

三、乐观锁实现

       一般是先给表加一个版本号(version)字段。当读取数据时,将version字段的值一同读出。每操作(更新)一次,将那条记录的version加1。当我们提交更新的时候,判断此刻的version值是否与刚刚查询出来的version值相等。如果相等,则说明这段期间,没有其他程序对其进行操作(更新)。则予以更新,将version字段的值加1;否则认为是过期数据,拒绝更新。

      举个生活中中例子,小A看上了隔壁班的班花,并开始了疯狂的追求。但被隔壁班的小B捷足先登,率先把女神追到手。没办法,虽然小B起步晚,但人家近水楼台先得月啊。此刻,小A心有不甘,嘴里念念有词:“我真傻,真的,明明是我先开始追求的。。”最终,小A只能灰溜溜的放弃啦。。。

      ①数据表设计  task  有三个字段,分别是id、value、version

      ②每次更新task表中的value字段,为了防止发生冲突,需要这样操作。

update task set value=newValue,version=versionValue+1  where id=1 and version=versionValue ;

      只有这条语句执行了,才表明本次更新value字段的值成功

       假如有两个节点A和B都要更新task表中的value字段,在同一时刻,A节点和B节点从task表中读到的version值为2,那么A节点和B节点在更新value字段值的时候,都执行update task set value=newValue,version=3 where id=1 and version=2;实际上只有1个节点执行该SQL语句成功,假设A节点执行成功,那么此刻task表的version字段的值是3,B节点在执行 update task set value=newValue,version=3 where id=1 and version=2 这条SQL语句是不执行的。这样就保证了更新task表时不发生冲突。

四、悲观锁实现

       要使用悲观锁,需要关闭mysql数据库的自动提交属性,因为mysql默认使用autocommit模式,也就是说,当你执行一个更新操作后,mysql会立即将结果进行提交。

        使用命令设置mysql 为非autocommit模式:set autocommit =0;

       一般使用select ... for update对所选择的数据进行加锁处理,例如,select status form t_goods where id in ('1','2')  for update;这条sql语句会锁定t_goods表中id为1和id为2的记录。

       使用场景举例:商品t_goods表有一个字段status,status为1代表商品未被下单,status为2代表商品已经被下单,那么我们队某个商品下单是必须确保该商品status为1.假设商品的id为1

①begin/begin work/start transaction;//开始事务
       ②select status from t_goods where id=1 for update;//查询出商品信息
       ③insert into t_orders(id,goods_id) value (null,1);//根据商品信息生成订单
       ④update t_goods set status=2;//修改商品status为2
       ⑤commit/commit work;//提交事务