这是我参与「第三届青训营 -后端场」笔记创作活动的第5篇笔记
事务
①原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
②一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
③隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
④持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
在Redis事务没有没有隔离级别的概念!
在Redis单条命令式保证原子性的,但是事务不保证原子性!
悲观锁是对数据的修改持悲观态度(认为数据在被修改的时候一定会存在并发问题),因此在整个数据处理过程中将数据锁定。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在应用层中实现了加锁机制,也无法保证外部系统不会修改数据)
锁机制是为了解决高并发问题。
使用悲观锁的原理就是,当我们在查询出goods信息后就把当前的数据锁定,直到我们修改完毕后再解锁。
要使用悲观锁,我们必须关闭mysql数据库的自动提交属性。
set autocommit=0;
关闭了mysql的autocommit,所以需要手动控制事务的提交。
使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。
如果无主键或者主键不明确,会锁住整个表。 没有提交事务导致行锁住 还是因为select…for update 锁住数据?
不是因为事务,而是因为select…for update 锁住数据。
乐观锁
①当程序中可能出现并发的情况时,就需要保证在并发情况下数据的准确性,以此确保当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的。
②没有做好并发控制,就可能导致脏读、幻读和不可重复读等问题。
在Redis是可以实现乐观锁的!
总结:乐观锁和悲观锁的区别。
悲观锁: 什么时候都会出问题,所以一直监视着,没有执行当前步骤完成前,不让任何线程执行,十分浪费性能!一般不使用!
乐观锁: 只有更新数据的时候去判断一下,在此期间是否有人修改过被监视的这个数据,没有的话正常执行事务,反之执行失败!
Redis的事务机制以及watch指令(CAS)实现乐观锁。
所谓乐观锁,就是利用版本号比较机制,只是在读数据的时候,将读到的数据的版本号一起读出来,当对数据的操作结束后,准备写数据的时候,再进行一次数据版本号的比较,若版本号没有变化,即认为数据是一致的,没有更改,可以直接写入,若版本号有变化,则认为数据被更新,不能写入,防止脏写。
基于redis实现乐观锁。
redis的事务,涉及到的指令,主要有multi,exec,discard。而实现乐观锁的指令,在事务基础上,主要是watch指令.
multi和exec之间的指令。 键值对变化时,指令不执行。
利用watch指令,基于CAS机制,简单的乐观锁。
watch指令在一次事务执行完毕后,即结束其生命周期。
基于redis的乐观锁,可以得出一个结论:
-
乐观锁的实现,必须基于WATCH,然后利用redis的事务。
-
WATCH生命周期,只是和事务关联的,一个事务执行完毕(执行了exec命令),相应的watch的生命周期即结束。
测试redis乐观锁机制,需要开启两个窗口。
incr 命令:对数值+1;
decr 命令:对数值-1;
Redis Decrby 命令将 key 所储存的值减去指定的减量值。 decrby key 20
Redis实现乐观锁比较简单,主要思路就是watch一个key的变动,并在watch和unwatch之间做一个类似事务操作,只有当事务操作成功,整体循环才会跳出,当然,当操作期间watch的key变动时候,提交事务操作时候,事务操作将会被取消。