浅谈Redis如何应对并发访问

目录
  • 前言
  • 原子性操作
    • 单命令模式
    • 多命令模式
      • lua简介
      • 建议
  • 事务
  • 加锁
  • 总结

前言

项目中经常会遇到这种场景,我们需要先将Redis数据读取到本地,然后进行修改,修改完成后在将数据写回Redis,这种读取-修改-写回操作,我们称之为RMW操作。当有多个客户端对同一份数据执行RMW操作的话,Redis如何保证RMW操作涉及的代码以原子性方式执行?

原子性操作

Redis的原子性操作是一种无锁操作,即可以保证并发控制,还能减少系统对并发性能的影响,

单命令模式

把Redis多个操作实现成一个操作,即为单命令模式。

Redis提供了INCR/DECR命令,可以对数据进行增值/减值操作,而且它们本身就是单个命令操作,Redis单线程模式,执行命令时具有互斥性。

示例说明

 public Long getIncrNumber(String key,long alive)
    {
        RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
        Long incrNum = entityIdCounter.getAndIncrement();
        if (null == incrNum || incrNum.longValue()==0)
        {
            entityIdCounter.expire(alive,TimeUnit.MILLISECONDS);
            incrNum = entityIdCounter.getAndIncrement();
        }
        return incrNum;
    }

说明:采用Reids的INCR命令,如果不存在的key则设置过期时间,如果key存在则进行递增操作返回。所以如果我们执行RMW操作进行相关的递增或者递减操作时,Redis提供的INCR和DECY命令可以保证并发控制。

多命令模式

当我们不是执行简单的加加减减操作,而是更加复杂的逻辑判断或者其他操作时,Redis是无法保证原子性,所以需要将多个操作写到一个Lua脚本中,Redis会把Lua脚本作为一个整体执行,在执行过程中不会被其他命令打断,从而保证了操作的原子性。

lua简介

Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

示例说明

接口进行限流操作,同一用户3秒内不能重复访问,我们可以通过lua脚本来实现。

local key = KEYS[1]
local count = tonumber(ARGV[1])
local time = tonumber(ARGV[2])
local current = redis.call('get', key)
if current and tonumber(current) > count then
    return tonumber(current)
end
current = redis.call('incr', key)
if tonumber(current) == 1 then
    redis.call('expire', key, time)
end
return tonumber(current)

说明:key、count、time为三个传入参数,分别代表Redis的key、次数和过期时间。通过get获取key对应的值,获取的值为时间内访问接口的次数,如果为第一次访问则返回的为null,此时需要对当前key进行自增1操作,如果返回为数字,则需要判断返回的数字是否已经超过了cout值,如果超过说明已经超过限流了,直接返回。

建议

  • 1.编写lua脚本不要进行复杂耗时的计算逻辑,否则执行lua时间过长,会导致Redis主线程阻塞。
  • 2.lua脚本尽量简单,不要把所有的操作都放入到lua脚本中执行,这样会导致Redis执行脚本的时间增加,同时也会降低Redis的并发性能。

事务

关于事务保证原子性,采用的watch命令其原理和乐观锁的实现原理类似,详情可以参考juejin.cn/post/712582… 文章,本文就不在具体阐述。

加锁

关于Redis的分布式锁的实现,后续的章节进行详情说明。高并发环境下加锁虽然能够保证正确性,但是也会带来其他的问题:

  • 加锁操作过多会降低系统的并发访问性能
  • Redis客户端要加锁,需要使用分布式锁,而分布式锁实现复杂,增加复杂度。

总结

本文讲解了针对并发访问Redis如何保证原子性操作,针对不同的业务场景,选择合适的方案,更多相关Redis 并发访问内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • php 使用redis锁限制并发访问类示例

    本文介绍了php 使用redis锁限制并发访问类,并详细的介绍了并发访问限制方法. 1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功. 例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制的情况下,用户则可以使用同一个换领码同时兑换到多张优惠券. 伪代码如下: if A(可以换领)     B(执行换领)     C(更新为已换领) D(结束) 如果用户并发提交换领码,都能通过可以换领(A)的判断,因

  • 浅谈Redis如何应对并发访问

    目录 前言 原子性操作 单命令模式 多命令模式 lua简介 建议 事务 加锁 总结 前言 项目中经常会遇到这种场景,我们需要先将Redis数据读取到本地,然后进行修改,修改完成后在将数据写回Redis,这种读取-修改-写回操作,我们称之为RMW操作.当有多个客户端对同一份数据执行RMW操作的话,Redis如何保证RMW操作涉及的代码以原子性方式执行? 原子性操作 Redis的原子性操作是一种无锁操作,即可以保证并发控制,还能减少系统对并发性能的影响, 单命令模式 把Redis多个操作实现成一个操

  • 浅谈Redis高并发缓存架构性能优化实战

    目录 场景1: 中小型公司Redis缓存架构以及线上问题实战 场景2: 大厂线上大规模商品缓存数据冷热分离实战 场景3: 基于DCL机制解决热点缓存并发重建问题实战 场景4: 突发性热点缓存重建导致系统压力暴增 场景5: 解决大规模缓存击穿导致线上数据库压力暴增 场景6: 黑客工资导致缓存穿透线上数据库宕机 场景7: 大V直播带货导致线上商品系统崩溃原因分析 场景8: Redis分布式锁解决缓存与数据库双写不一致问题实战 场景9: 大促压力暴增导致分布式锁串行争用问题优化 场景10: 利用多级缓

  • 浅谈Redis的事件驱动模型

    Redis 作为一个 Client-Server 架构的数据库,其源码中少不了用来实现网络通信的部分.而你应该也清楚,通常系统实现网络通信的基本方法是使用Socket编程模型,,包括创建 Socket.监听端口.处理连接请求和读写请求.但是,由于基本的 Socket 编程模型一次只能处理一个客户端连接上的请求,所以当要处理高并发请求时,一种方案就是使用多线程,让每个线程负责处理一个客户端的请求. 而 Redis 负责客户端请求解析和处理的线程只有一个,那么如果直接采用基本 Socket 模型,就

  • 浅谈Redis缓存击穿、缓存穿透、缓存雪崩的解决方案

    目录 前言 Redis缓存使用场景 Redis缓存穿透 解决方案 1.对空值缓存 2.添加参数校验 3.采用布隆过滤器 Redis缓存雪崩 解决方案 1.大量热点数据同时失效带来的缓存雪崩问题 2. 服务降级 3. Redis 缓存实例发生故障宕机带来的缓存雪崩问题 Redis缓存击穿 解决方案 1. 热key不过期 2. 分布式锁 总结 缓存击穿 缓存穿透 缓存雪崩 前言 在日常的项目中,缓存的使用场景是比较多的.缓存是分布式系统中的重要组件,主要解决在高并发.大数据场景下,热点数据访问的性能

  • 浅谈redis缓存在项目中的使用

    背景 Redis 是一个开源的内存数据结构存储系统. 可以作为数据库.缓存和消息中间件使用. 支持多种类型的数据结构. Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence). 通过 Redis 哨兵(Sentinel)和 Redis 集群(Cluster)的自动分区,提供高可用性(high availability). 基本数

  • 浅谈Redis 缓存的三大问题及其解决方案

    目录 一.缓存穿透 1. 常见解决方案 2. 布隆过滤器 3. 缓存空数据与布隆过滤器的比较 二.缓存击穿 解决方案 三.缓存雪崩 解决方案 Redis 经常用于系统中的缓存,这样可以解决目前 IO 设备无法满足互联网应用海量的读写请求的问题. 一.缓存穿透 缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起 id 为-1 的数据或者特别大的不存在的数据.有可能是黑客利用漏洞攻击从而去压垮应用的数据库. 1. 常见解决方案 对于缓存穿透问题,常见的解决方案有以下三种: 验证拦截:

  • 浅谈Redis缓冲区机制

    目录 Redis缓冲区机制 客户端缓冲机制 应对输入缓冲区溢出 查看输入缓冲区信息 如何解决输入缓冲区溢出 应对输出缓冲区溢出 Monitor命令的执行 输出缓冲区设置不合理 主从集群中的缓冲区 复制缓冲区 复制缓冲区溢出 复制积压缓冲区 解决复制积压缓冲区溢出 Redis缓冲区机制 Redis中的缓冲区机制就是为了平衡客户端发送命令和服务端处理命令的速度差异,如果客户端写入过快或者服务端读取过慢这就会导致缓冲区溢出,缓冲区一旦溢出将引发一系列的性能问题,下面我们详细聊聊. 客户端缓冲机制 Re

  • 浅谈php处理后端&接口访问超时的解决方法

    [HTTP访问] 一般我们访问HTTP方式很多,主要是:curl, socket, file_get_contents() 等方法. 如果碰到对方服务器一直没有响应的时候,我们就悲剧了,很容易把整个服务器搞死,所以在访问http的时候也需要考虑超时的问题. [ CURL 访问HTTP] CURL 是我们常用的一种比较靠谱的访问HTTP协议接口的lib库,性能高,还有一些并发支持的功能等. CURL: curl_setopt($ch, opt) 可以设置一些超时的设置,主要包括: *(重要) CU

  • 浅谈Redis的几个过期策略

    概述 设置过期时间 expire key time(以秒为单位) 这是最常用的方式 setex(String key, int seconds, String value) 字符串独有的方式 除了字符串自己独有设置过期时间的方法外,其他方法都需要依靠expire方法来设置时间如果没有设置时间,那缓存就是永不过期如果设置了过期时间,之后又想让缓存永不过期,使用persist key 三种过期策略 定时删除 在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对

  • 浅谈Redis主从复制以及主从复制原理

    面临问题 1. 机器故障.我们部署到一台 Redis 服务器,当发生机器故障时,需要迁移到另外一台服务器并且要保证数据是同步的.而数据是最重要的,如果你不在乎,基本上也就不会使用 Redis 了. 2. 容量瓶颈.当我们有需求需要扩容 Redis 内存时,从 16G 的内存升到 64G,单机肯定是满足不了.当然,你可以重新买个 128G 的新机器. 解决办法 要实现分布式数据库的更大的存储容量和承受高并发访问量,我们会将原来集中式数据库的数据分别存储到其他多个网络节点上.Redis 为了解决这个

随机推荐