技术栈1 - Redis(3) - redis事务

2019/01/10

P56:

基本的redis事务

1、概念:
  1、redis层面:
    在redis中,被multi和exec命令包围的命令会一个接一个执行,知道所有命令都执行结束之后才会处理其他客户端的命令。
    1、先执行multi命令
    2、接到multi命令后,将客户端发送的命令放入到一个队列里面,知道客户端发送exec命令结束
    3、redis不被打断的一个一个的执行队列中的命令;
  2、python层面:
    redis事务在python客户端上是由流水线(pipeline)实现的。
    pipeline()相当于multi,execute()相当于exec命令。

    1、pipeline = conn.pipeline() 开启一个事务;
    2、pipeline.incr 增加命令到队列
    3、pipeline.execute() 执行命令。

2、优点
  1、移除竞争条件
  2、提高性能
    redis每条命令都是客户端和服务器的一次通信,pipeline可以一次通信执行所有的命令。

p76:
2、redis事务

  1、关系型事务 和 redis事务
    关系型事务:
      1、用户向数据库发送begin
      2、执行相互一致(consistent)的写操作和读操作
      3、commit提交修改 或者 rollback回滚操作。
    
    redis事务如上所示。
    问题:redis只有执行exec命令了才会执行,在此之前数据不会发生变化,所以在exec执行之前就无法判断是不是数据一致,因为没有实质操作数据。
    redis命令组合:
      multi 和 exec
      watch对键进行监视; pipe.watch (键应该就是某一次事务操作的标识)
      discard 忽略已经加入队列的某条命令

  2、乐观锁(pessimistic locking) 和 悲观锁(optimistic locking)
    1、redis为什么没实现典型的加锁操作?
      悲观锁,问题是持有锁的客户端执行多久,等待解锁的客户端就要阻塞多久。

      乐观锁是如果其他客户端抢先修改了数据,redis只会通知执行了watch命令的客户端,然后客户端失败重试就可以了,而不必花时间等待。
      (watch观察者模式,客户端被通知)

非事务型 流水线

不使用事务,但使用流水线,目的是为了减少通信次数

python实现:

conn.pipeline(True) # 使用事务

conn.pipeline(False) # 不使用事务

其他改善性能的选项

命令: redis-benchmark (-c l -q)

展示一些常用命令在1秒内可以执行的次数。

一般python客户端是所示性能的50%~60%。

知识点学习: 乐观锁

 乐观锁,大多是基于数据版本Version记录机制实现。
 何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中, 一般是通过为数据库表增加一个 “version” 字段来实现。
 读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,
 如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

 乐观锁 总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,
 但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。

 CAS操作方式:即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。
 当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。

  悲观锁
 总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。
 可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁,在Java中,synchronized的思想也是悲观锁。

 二者最大的区别就是其他访问锁的线程需不需要阻塞挂起。

乐观锁和悲观锁:
 乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
 不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。
 但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。

 乐观锁机制避免了长事务中的数据库加锁开销,大大提升了大并发量下的系统整体性能表现。

参考

乐观锁VS悲观锁优缺点总结


Show Disqus Comments