2019独角兽企业重金招聘Python工程师标准>>>

1. Redis事务机制

1. 与MySQL等关系数据库相同,Redis中也有事务机制,Redis的事务实质上是命令的集合,但Redis中的事务机制不保证事务的原子性,这与关系型数据库中的事务不同,在一个事务中要么所有命令都被执行,要么所有事物都不执行。 一个事务从开始到执行会经历以下三个阶段:

  • 开始事务。
  • 命令入队。
  • 执行事务。

在MySQL中使用START TRANSACTION 或 BEGIN开启一个事务,使用COMMIT提交一个事务;而在Redis中使用MULTI 开始一个事务,由 EXEC 命令触发事务, 一并执行事务中的所有命令。和关系型数据库中的事物相比,在redis事务中如果有某一条命令执行失败,其它的命令仍然会被继续执行,也就是Redis中不支持事务的回滚,也就不具备事务的原子性

2. Redis事务机制的相关指令:

  • MULTI:用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才会被原子执行
  • EXEC:执行命令队列中的所有命令,但如果在一个事务内执行了WATCH命令,那么只有当WATCH所监控的keys没有被修改的前提下,EXEC命令才能执行事务队列中的所有命令,否则EXEC将放弃当前事务中的所有命令。
  • DISCARD:取消执行事务队列中的所有命令,同时再将当前连接的状态恢复为正常状态,即非事务状态。如果WATCH命令被使用,该命令将UNWATCH所有的keys。注意,该指令并不是Redis的回滚指令,Redis中不支持回滚,该指令只是取消事务中的所有指令的执行
  • WATCH  key[key...]:在MULTI命令执行之前,可以指定待监控的keys,在执行EXEC之前,如果被监控的keys发生修改,EXEC将放弃执行该事务队列中的所有指令。WATCH命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC或DISCARD命令。该命令可以保证某个key的CAS
  • UNWATCH:取消当前事务中指定监控的keys,如果执行了EXEC或DISCARD命令,则无需再手工执行该命令了,因为在此之后,事务中所有的keys都将自动取消监控

3. 命令使用示例:

//正常执行
127.0.0.1:6379> redis-cli -h 127.0.0.1 -p 6379    //命令拼接redis服务器
ok
127.0.0.1:6379> get test                                      //获取test的键值
"hello world"
127.0.0.1:6379> multi          //生成事务
ok
127.0.0.1:6379> set test "hello mygod"               //修改指令
QUEUED
127.0.0.1:6379>exec                                           //提交事务
1) OK
127.0.0.1:6379>

2. 分布式锁

1. 产生背景:分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。在单机环境中,Java中其实提供了很多并发处理相关的API,但是这些API在分布式场景中就无能为力了。也就是说单纯的Java Api并不能提供分布式锁的能力。所以针对分布式锁的实现目前有多种方案。

2. 实现分布式锁的方案:典型的方案有以下几种

  • 基于数据库实现分布式锁
  • 基于缓存(redis,memcached,tair)实现分布式锁
  • 基于Zookeeper实现分布式锁

3. 分布式锁的要求:

  • 可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行
  • 这把锁要是一把可重入锁(避免死锁)
  • 这把锁最好是一把阻塞锁(根据业务需求考虑要不要这条)
  • 有高可用的获取锁和释放锁功能
  • 获取锁和释放锁的性能要好

3. 分布式锁的数据库实现方案

1. 基于数据库表的实现:最简单的方式可能就是直接创建一张锁表,然后通过操作该表中的数据来实现,当我们要锁住某个方法或资源时,我们就在该表中增加一条记录,想要释放锁的时候就删除这条记录。

  • 首先创建一个分布式锁的表,可以把里面存储的看做分布式锁

    CREATE TABLE `methodLock` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`method_name` varchar(64) NOT NULL DEFAULT '' COMMENT '锁定的方法名',`desc` varchar(1024) NOT NULL DEFAULT '备注信息',`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存数据时间,自动生成',PRIMARY KEY (`id`),UNIQUE KEY `uidx_method_name` (`method_name `) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='锁定中的方法';
    
  • 如果想要对分布式执行的某个方法加锁,就使用这个方法名向表中插入数据

    insert into methodLock(method_name,desc) values (‘method_name’,‘desc’)

    因为我们对method_name做了唯一性约束,这里如果有多个插入请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,可以执行方法体内容。

  • 当方法执行完毕之后,想要释放锁的话,需要执行以下Sql删除锁
    delete from methodLock where method_name ='method_name'

2. 产生的问题:

  • 这把锁强依赖数据库的可用性,数据库是一个单点,一旦数据库挂掉,会导致业务系统不可用。
  • 这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁。
  • 这把锁只能是非阻塞的,因为数据的insert操作,一旦插入失败就会直接报错。没有获得锁的线程并不会进入排队队列,要想再次获得锁就要再次触发获得锁操作。
  • 这把锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在了。

3. 解决办法:

  • 数据库是单点?搞两个数据库,数据之前双向同步。一旦挂掉快速切换到备库上。
  • 没有失效时间?只要做一个定时任务,每隔一定时间把数据库中的超时数据清理一遍。
  • 非阻塞的?搞一个while循环,直到insert成功再返回成功。
  • 非重入的?在数据库表中加个字段,记录当前获得锁的机器的主机信息和线程信息,那么下次再获取锁的时候先查询数据库,如果当前机器的主机信息和线程信息在数据库可以查到的话,直接把锁分配给他就可以了。

4. 基于Redis缓存实现的分布式锁

1. 相比较于基于数据库实现分布式锁的方案来说,基于缓存来实现在性能方面会表现的更好一点(连接数据库进行读写操作性能耗费比缓存大)。而且很多缓存是可以集群部署的,可以解决单点问题。

2. Redis中有直接的命令支持,而且Redis的本身命令执行是一个单线程的,这就为分布式锁提供了很好的实现,实现命令如下

  • SETNX key val:当且仅当key不存在时,set才会成功,返回1;若key存在,操作失败,返回0。
  • expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。
  • delete key:删除key,即释放锁

3. Jedis客户端也提供了相应的方法,主要就是setnx(String key,String value)方法,简单实现思想伪代码如下:

String get(String key) {
//首先尝试从redis(或redis集群)中获取key对应的数据  String value = redis.get(key);
//如果为null,则使用redis中的分布式锁if (value  == null) {
//通过setnx方法创建分布式锁if (redis.setnx(key_mutex, "1")) {  // 设置分布式锁的过期时间,可以避免死锁 redis.expire(key_mutex, 3 * 60)  value = db.get(key); //从数据库中取得数据 redis.set(key, value);//回写到缓存中  redis.delete(key_mutex);//释放锁  } else {  //其他线程休息50毫秒后重试  Thread.sleep(50);  get(key);  }  }
}  

5. 基于Zookeeper实现分布式锁

1. ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:

(1)创建一个目录mylock; 
(2)线程A想获取锁就在mylock目录下创建临时顺序节点; 
(3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁; 
(4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点; 
(5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。

这里推荐一个Apache的开源库Curator,它是一个ZooKeeper客户端,Curator提供的InterProcessMutex是分布式锁的实现,acquire方法用于获取锁,release方法用于释放锁。

优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。

缺点:因为需要频繁的创建和删除节点,性能上不如Redis方式

转载于:https://my.oschina.net/ProgramerLife/blog/2875640

Redis应用学习——Redis事务与实现分布式锁相关推荐

  1. Redis应用详解(一)分布式锁

    1. 前言 在某些场景中,多个进程必须以互斥的方式独占共享资源,这时用分布式锁是最直接有效的. 随着技术快速发展,数据规模增大,分布式系统越来越普及,一个应用往往会部署在多台机器上(多节点),在有些场 ...

  2. 深度解析串行并发并行,开发人员需彻底搞懂丨mysql|redis|skynet|协程|索引|读写分离|分布式锁|主从同步

    深度解析串行并发并行,开发人员需彻底搞懂 视频讲解如下,点击观看: 深度解析串行并发并行,开发人员需彻底搞懂丨mysql|redis|skynet|协程|索引|读写分离|分布式锁|主从同步丨C/C++ ...

  3. Redis面试常问3 如何实现分布式锁 记住Redis的原子性

    Redis面试常问3 如何实现分布式锁 上面的伪代码有问题 从 Redis 2.6.12 版本开始, SET 命令的行为可以通过一系列参数来修改: http://redisdoc.com/string ...

  4. 【2020尚硅谷Java大厂面试题第三季 04】Redis 9种数据类型使用场景,分布式锁演变步骤,lua脚本,redis事务,Redisson,Redis内存占用,删除策略,内存淘汰策略,手写LRU

    1.安装redis6.0.8 2023 02 02 为:redis-7.0.8.tar.gz 2.redis传统五大数据类型的落地应用 3.知道分布式锁吗?有哪些实现方案?你谈谈对redis分布式锁的 ...

  5. Redis事务机制和分布式锁

    Redis事务机制 严格意义来讲,Redis的事务和我们理解的传统数据库(如mysql)的事务是不一样的:Redis的事务实质上是命令的集合,在一个事务中要么所有命令都被执行,要么所有事物都不执行. ...

  6. 【Redis | 黑马点评 + 思维导图】分布式锁

    文章目录 分布式锁的基本原理和实现方式对比 Redis分布式锁的实现核心思路 分布式锁的初级实现 Redis分布式锁误删情况说明 解决Redis分布式锁误删问题 分布式锁的原子性问题 Lua脚本解决多 ...

  7. java如何保证redis设置过期时间的原子性_分布式锁用 Redis 还是 Zookeeper

    在讨论这个问题之前,我们先来看一个业务场景: 系统A是一个电商系统,目前是一台机器部署,系统中有一个用户下订单的接口,但是用户下订单之前一定要去检查一下库存,确保库存足够了才会给用户下单. 由于系统有 ...

  8. redis获取存在的键值_Redis 分布式锁、限流

    谈及分布式系统的时候,很多企业都会使用到分布式锁,分布式锁的实现方案目前主要是存在两种,redis实现和zookeeper实现. redis实现方案是多个线程相互竞争,在redis的某一个节点内创建一 ...

  9. Redis的n种妙用,分布式锁,分布式唯一id,消息队列,抽奖……

    介绍 redis是键值对的数据库,常用的五种数据类型为字符串类型(string),散列类型(hash),列表类型(list),集合类型(set),有序集合类型(zset) Redis用作缓存,主要两个 ...

最新文章

  1. dataset string dataset
  2. 【论文解读】CIKM20-MiNet:阿里|跨域点击率预估混合兴趣模型
  3. html按键清空怎么写_html的空格代码怎么写?教你如何使用空格nbsp代码
  4. SAP CDS view 单元测试框架 Test Double 介绍
  5. asp.net中实现群发邮件功能
  6. python里turtle.circle什么意思_Python turtle.circle方法代碼示例
  7. Springboot中使用websocket发送信息给指定用户和群发
  8. 转载:Linux批量远程管理主机命令_pssh用法详解
  9. c语言第一周项目,C语言第一周实战
  10. 我发现混的好的,都是挺能“吹牛逼”的
  11. 克莱姆法则(cramer法则)详解
  12. 好用的数据建模工具,探索中完善
  13. unity直播推流方式_干货,抖音无人直播技术(建议收藏)
  14. 零基础学习 iOS 开发?如何系统学习 iOS ?
  15. windows10常见故障排查
  16. 若依 后台框架配置丛数据源使用
  17. JavaScript 常用事件大全
  18. 高一到高三计算机笔记,高中数学笔记总结高一至高三,很全.doc
  19. Unity3D Shader 新手教程(1/6)
  20. 商贸零售行业2021年投资策略:市场下沉、渠道效率升级,新品牌新业态乘风而起

热门文章

  1. springBoot入门第一章springBoot第一个程序
  2. weblogic管理3 - 生产模式下免密码管理配置
  3. Linux命令(20)linux服务器之间复制文件和目录
  4. Ubuntu 16.04 安裝chrome
  5. Java Switch语句及性能剖析(转载补充)
  6. Openfire:安装指南
  7. 只谈Network,不谈Social,互联网营销
  8. 缺少ntstrsafe.lib kndis5mp.lib解决办法
  9. 企业开展网络营销常用的十九种方法
  10. 配置VSS2005的Internet访问