程序员必懂的Redis技术实战

课程推荐:前端开发工程师--学习猿地精品课程

Redis是现在很受欢迎的NoSQL数据库之一,目前广泛用于缓存系统、分布式锁、计数器、消息队列系统、排行榜、社交网络等场景中,本篇文章成哥为大家带来redis日常使用实践,及通过代码实现redis的分布式锁。

01 Redis简介
Redis是一个开源使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可以持久化的日志类型、key-value数据库,并提供多种语言的API。Redis的出现,很大程度上弥补了Memcache这类key/value存储的不足,在部分场合可以对关系型数据库(MySql、DB2等,关系型数据库通过外键关联来建立表与表之间的关系。非关系型数据库通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定)起到很好的补充作用。(如可降低数据库访问压力,弊端冷数据的处理)。

02 Redis实现特点
(1)单线程

Redis是通过单线程实现的,单线程避免了多线程的切换性能损耗问题,同时它所有的数据都在内存中,所有的运算都是内存级别的运算,所以即使是单线程redis还能这么快。但也正是因为使用的是单线程,所以要小心使用 Redis 指令,对于那些耗时的指令(比如keys),一定要谨慎使用,一不小心就可能会导致 Redis 卡顿。

(2)IO多路复用

Redis通过IO多路复用解决单线程下并发客户端的访问,redis利用epoll来实现IO多路复用,将连接信息和事件放到队列中,依次放到文件事件分派器,事件分派器将事件分发给事件处理器。具体架构如下:
file
程序员必懂的Redis技术实战
03 Redis集群方案比较
(1)哨兵模式
file
程序员必懂的Redis技术实战
在redis3.0以前的版本要实现集群一般是借助哨兵sentinel工具来监控master节点的状态,如果master节点异常,则会做主从切换,将某一台slave作为master,哨兵的配置略微复杂,并且性能和高可用性等各方面表现一般,特别是在主从切换的瞬间存在访问瞬断的情况,而且哨兵模式只有一个主节点对外提供服务,没法支持很高的并发,且单个主节点内存也不宜设置得过大,否则会导致持久化文件过大,影响数据恢复或主从同步的效率 。

(2)高可用集群模式
file
程序员必懂的Redis技术实战
redis高可用集群是一个由多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片特性。Redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到上万个节点(官方推荐不超过1000个节点)。redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单。

04 Redis集群部署常见问题
在了解Redis集群部署常见问题之前我们先来了解一下Redis集群的实现原理。Redis Cluster 将所有数据划分为 16384 的 slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每个节点中,当 Redis Cluster 的客户端来连接集群时,它也会得到一份集群的槽位配置信息并将其缓存在客户端本地。这样当客户端要查找某个 key 时,可以直接定位到目标节点。同时因为槽位的信息可能会存在客户端与服务器不一致的情况,还需要纠正机制来实现槽位信息的校验调整。

(1)跳转重定位问题

当客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。客户端收到指令后除了跳转到正确的节点上去操作,还会同步更新纠正本地的槽位映射表缓存,后续所有 key 将使用新的槽位映射表。

(2)网络抖动问题

在生产环境中网络抖动问题不可避免,为解决这种问题,Redis Cluster 提供了一种选项cluster­node­timeout,表示当某个节点持续 timeout 的时间失联时,才可以认定该节点出现故障,需要进行主从切换。如果没有这个选项,网络抖动会导致主从频繁切换 (数据的重新复制)。

05 代码实现基于Redis的分布式锁
在多个进程/线程对同一个共享资源读写场景下,会因为资源的争夺而出现混乱,导致数据不一致。为了避免该问题我们可以在进程/线程在操作共享资源前获取一个令牌(也就锁),只有获取了该令牌的进程/线程才可以操作资源,在操作完资源后释放该令牌。这就实现了分布式锁。

Redis的分布式锁是基于Redis SETNX命令来实现的,在Redis中通过SETNX命令设置Key Value时有如下两种结果:

1)返回1,表示为指定的key设置值成功,也即表示当前进程已经获取了锁资源

2)返回0,表示为指定的key设置值失败,因为当前已存在该key,也即表示其它进程获取了锁资源

下面我们就来看看怎么通过python实现分布式锁吧

(1)首先我们创建一个不使用分布式锁的示列,通过多线程对全局变量进行加1操作看看结果如何,具体代码如下:
file
程序员必懂的Redis技术实战
代码运行结果如下,发现不是我们预期的值(预期值应为3+3=6)
file
程序员必懂的Redis技术实战
(2)接着我们创建带分布式锁的示列,我们先来看看分布式锁创建的方法,具体如下

  1. import time
  2. import uuid
  3. from redis import StrictRedis, ConnectionPool
  4. import threading
  5. class CollectRedis:
  6. 创建redis操作类

  7. def init(self):
  8. self.host = "1.1.1.1"
  9. self.port = 6379
  10. self.db = 5
  11. @property
  12. def redis_session(self):
  13. _session = getattr(self, "__redis_session", None)
  14. if _session:
  15. return _session
  16. redis_pool = ConnectionPool(host=self.host, port=self.port, db=self.db)
  17. _session = StrictRedis(connection_pool=redis_pool)
  18. setattr(self, "__redis_session", _session)
  19. return _session
  20. 获取锁

  21. def get_lock(self, lock_key):
  22. return self.redis_session.get(lock_key)
  23. 设置锁

  24. def set_lock(self, lock_key, value, timeout=300):
  25. session = self.redis_session
  26. tag = session.setnx(lock_key, value)
  27. 如果key能创建成功则为该key设置一个超时时间,这个相当于锁的有效时间

  28. 如果没有超时时间则会导致程序死锁

  29. if tag:
  30. session.expire(lock_key, timeout)
  31. return tag
  32. 删除锁也就是释放锁

  33. def delete_lock(self, lock_key):
  34. return self.redis_session.delete(lock_key)
  35. 获取锁资源方法

  36. def acquire_lock(lock_name, time_out=300):
  37. identifier = str(uuid.uuid4())
  38. end = time.time() + time_out + 30
  39. redis_connect = CollectRedis()
  40. 如果不能获取锁资源则线程一直挂起直到获取锁资源或者超时

  41. while time.time() < end:
  42. if redis_connect.set_lock(lock_name, identifier, timeout=time_out):
  43. return identifier
  44. time.sleep(0.01)
  45. return False
  46. 释放锁资源

  47. def release_lock(lock_name, identifier):
  48. redis_connect = CollectRedis()
  49. value = redis_connect.get_lock(lock_name)
  50. if not value:
  51. return True
  52. if value == identifier:
  53. redis_connect.delete_lock(lock_name)
  54. return True
  55. return False
  56. def resource_lock(lock_name, timeout=10):
  57. """
  58. 并发锁装饰器函数
  59. :param lock_name:
  60. :param timeout:
  61. :return:
  62. """
  63. def _outfunc(func):
  64. def inner_func(args, *kwargs):
  65. identifier = acquire_lock(lock_name, time_out=timeout)
  66. if not identifier:
  67. raise Exception("获取({})锁资源失败".format(lock_name))
  68. try:
  69. result = func(args, *kwargs)
  70. release_lock(lock_name, identifier)
  71. except Exception as e:
  72. 程序出现异常时主动释放锁资源

  73. release_lock(lock_name, identifier)
  74. raise Exception(e.args)
  75. return result
  76. return inner_func
  77. return _outfunc
    (3)最后我们在计算函数中增加分布式锁装饰器,然后查看程序运行结果是否符合预期,具体如下
    file
    file

06 总结
本篇文章主要带大家了解了Redis的一些特点、部署方案、集群中容器遇到的问题及如何基于redis实现分布式锁等内容,如果喜欢本篇文章不要忘了点赞、关注与转发哦!

文章转载自:https://developer.51cto.com/art/202011/631542.htm

Image placeholder
lafans
未设置
  0人点赞

没有讨论,发表一下自己的看法吧

推荐文章
css底层什么技术实现的

css底层什么技术实现的css底层是由浏览器进行解析渲染实现的。具体是通过css解析器解析css语法,生成css规则树,再结合dom树进行渲染绘制到屏幕上的。(相关课程推荐:css视频教程)CSS的渲

程序员必备工具箱

作为一名初级开发人员,我们经常会遇到许多不同的技术,对我们来说都是新鲜和陌生的,伴随着总有人说这将是下一个热点,我们必须学习。类似的话我已经听了好多年了,并且已经找到了一些模式,可以适当的抽象出你的工

这波技术社区的程序员,技术视野有点堪忧!

前一段时间写了一篇文章《凌晨1点突发致命生产事故,人工多线程来破局!》,只是一篇生产事故的记实文章,没想到在圈内流传甚广,其中有程序员对其中的细节有点疑惑,刚好国庆可以和大家再进一步探讨一下。现在技术

5位女性程序员的自白:计算机不撒谎;女程序员的代码一样也很棒

谁说这个领域就是男性的天下偏见本身就是一种带标签的想法她们的世界里只信奉“computer never lies”她们认为代码漂亮比发型漂亮更重要她们到底是谁?谷悦是喜欢简单、纯粹工作的气质女神,八年

怎么删除创建的react应用

怎么删除创建的react应用创建的react项目会保存在一个文件夹里,整个文件夹就是一个react项目,我们只需要将文件夹删除,即可删除react项目。下面是具体的操作方法:●右键文件夹,移动到回收站

如何运行别人的react项目

如何运行别人的react项目1、从网上下载项目到本地,放在自己的工作空间中2、下载好的项目缺少项目依赖环境,我们需要自己安装。npminstall这个步骤将会新建该项目的node_modules文件夹

如何访问已启动的react项目

如何访问已启动的react项目1、运行react项目都会开启一个终端窗口,只需要打开窗口,查看项目的运行地址即可。上面这个项目的地址是http://localhost:3000/,打开浏览器输入这个地

基于Tcp协议与基于Http协议的RPC简介笔记

前言:之前对于RPC方面的学习多限于对RMI原理的学习,直到今天在看陈康贤前辈的《大型分布式网站架构-设计与实践》这本书的时候,才发现原来RPC可以基于TCP协议也可以基于HTTP协议(这里所说的TC

你应该知道的RocketMQ

1.概述在很久之前写过一篇Kafka相关的文章,你需要知道的Kafka,那个时候在业务上更多的是使用的是Kafka,而现在换了公司之后,更多的使用的是Rocketmq,本篇文章会尽力全面的介绍Rock

SQL优化案例-定位系统中大量的rollback(十八)

系统中logfilesync比较严重,查看存储都没有问题,logfileparallelwrite很低,时间分布直方图也没问题数据库中提交和回滚操作比较频繁,每秒1000多次,rollback占比1/

BAT大牛推荐开发人员必备Spring源码剖析文档,深度剖析Spring

为什么学习读源码我们每天都和代码打交道。经过数年的基础教育和职业培训,大部分程序员都会「写」代码,或者至少会抄代码和改代码。但是,会读代码的并不在多数,会读代码又真正读懂一些大项目的源码的,少之又少。

老程序员肺腑忠告:千万别一辈子靠技术生存!

作为一个多年开发经验的老伙计,当回过头来想一想自己,觉得特别想对那些初学JAVA/DOT、NET技术的朋友说点心里话,希望你们能从我们的体会中,多少受点启发。 1一个程序员正确的自我心态究竟是什么样?

程序员的遮羞布:这个需求技术上无法实现

老读者都知道的,我在三线小镇洛阳工作。公司很小,开发人员最多的时候也就十来个,最少的时候也就我一个光杆司令。由于掌握着公司所有的核心代码,所以我一直很嚣张。老板也拿我没办法,有时候为了追加一个功能,还

把 14 亿中国人都拉到一个微信群,程序员在技术上能实现吗?

根据国家统计局的数据,截至2017年末,中国大陆总人口为13亿9008万人(包括31个省、自治区、直辖市和中国人民解放军现役军人,不包括香港、澳门和台湾以及海外华侨人数),早已超过13亿。目前,微信群

全网最通俗易懂的Kafka入门

前言只有光头才能变强。文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y众所周知,消息队列的产品有好几种,这里我选择学习Kafka

终于有篇看的懂的 B 树文章了!

索引,相信大多数人已经相当熟悉了,很多人都知道MySQL的索引主要以B+树为主,但是要问到为什么用B+树,恐怕很少有人能把前因后果讲述完整。本文就来从头到尾介绍下数据库的索引。索引是一种数据结构,用于

宝宝也能看懂的 leetcode 周赛 - 169 - 2

1305.AllElementsinTwoBinarySearchTreesHi大家好,我是张小猪。欢迎来到『宝宝也能看懂』系列之leetcode题解。这里是第169期的第2题,也是题目列表中的第13

宝宝也能看懂的 leetcode 周赛 - 169 - 1

1304.FindNUniqueIntegersSumuptoZeroHi大家好,我是张小猪。欢迎来到『宝宝也能看懂』系列之leetcode题解。这里是第169期的第1题,也是题目列表中的第1304题

宝宝也能看懂的 leetcode 周赛 - 169 - 3

1306.JumpGameIIIHi大家好,我是张小猪。欢迎来到『宝宝也能看懂』系列之leetcode题解。这里是第169期的第3题,也是题目列表中的第1306题--『JumpGameIII』题目描述

宝宝也能看懂的 leetcode 周赛 - 169 - 4

1307.VerbalArithmeticPuzzleHi大家好,我是张小猪。欢迎来到『宝宝也能看懂』系列之leetcode题解。这里是第169期的第4题,也是题目列表中的第1307题--『Verba

前端都该懂的浏览器工作原理,你懂了吗?

课程推荐《前端开发工程师--学习猿地精品课程》 前言在我们面试过程中,面试官经常会问到这么一个问题,那就是从在浏览器地址栏中输入URL到页面显示,浏览器到底发生了什么?这个问题看起来是老生常谈,但是这

93.7% 的程序员!竟然都不知道 Redis 为什么默认16个数据库?

▍导读在实际项目中Redis常被应用于做缓存,分布式锁、消息队列等。但是在搭建配置好Redis服务器后很多朋友应该会发现和有这样的疑问,为什么Redis默认建立了16个数据库,如下图所示。椐调查发现:

冬虫夏草之技术路线图之一【“技”——技术篇】

作为一名28年证券机构从业经历的老兵,杨松一直在观察和研究IT技术对金融机构的业务重构,以及证券业务变革相关的内容。今天,让我们来看看这位金融业内人士如何利用他28年的行业积累,通过“技”“术”“路”

“我是技术总监,你干嘛总问我技术细节?”

题图:fromZoommy每个周末的午后,把儿子送进EF读书,随后找个环境幽静的咖啡馆坐一会,这便是我一周中最放松的时光。在咖啡厅的气氛和环境这两点上,我似乎有强迫症,比如装修主色调的运用,地上装饰是

2019年度IT168技术卓越奖名单:技术开发类

与边缘计算、人工智能、量子计算、区块链等高大的技术不同,以ERP、CRM、BI等为代表的应用类软件正在以更创新、更接地气的方式,深入到各个行业。所以,PaaS正在成为云时代的主角。基于PaaS,Saa