• redis


    目录

    redis的使用场景

    redis的数据类型及使用场景

    redis的3种删除策略

    redis的6种数据淘汰策略

    Redis 持久化

    缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级

    Mysql和Redis的双写一致性

    单线程redis为什么这么快

    Redis 为什么是单线程的

    Redis 内部结构

    Redis三种集群模式

    ​编辑Redis 集群模式的工作原理能说一下么?在集群模式下,Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?

    了解一致性 hash 算法吗?如何动态增加和删除一个节点?

    Redis 常见性能问题和解决方案?

    Redis的IO多路复用

    Redis事务

    redis批量处理数据


    redis的使用场景


    1.热点数据的缓存
    2.限时业务的运用,redis中可以使用expire命令设置一个键的生存时间,到时间后redis会删除它。利用这一特性可以运用在限时的优惠活动信息、手机验证码等业务场景。
    3.计数器相关问题:限制一个手机号发多少条短信
    4.排行榜相关问题:借助redis的SortedSet进行热点数据的排序
    5.分布式锁
    6.延时操作(类似于MQ)
    7.点赞、好友等相互关系的存储
    8.队列(由于redis有list push和list pop这样的命令,所以能够很方便的执行队列操作)

    redis的数据类型及使用场景


    String
    这个其实没啥好说的,最常规的set/get操作,value可以是String也可以是数字。一般做一些复杂的计数功能的缓存。
    hash
    这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。
    list
    使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好。本人还用一个场景,很合适—取行情信息。就也是个生产者和消费者的场景。LIST可以很好的完成排队,先进先出的原则。
    set
    因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。为什么不用JVM自带的Set进行去重?因为我们的系统一般都是集群部署,使用JVM自带的Set,比较麻烦,难道为了一个做一个全局去重,再起一个公共服务,太麻烦了。
    另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。
    sorted set
    sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取TOP N操作。

    redis的3种删除策略


    1.定时删除:在设置键的过期时间的同时,创建一个定时任务,当键达到过期时间时,立即执行对键的删除操作
    2.惰性删除:放任键过期不管,但在每次从键空间获取键时,都检查取得的键是否过期,如果过期的话,就删除该键,如果没有过期,就返回该键
    3.定期删除:每隔一点时间,程序就对数据库进行一次检查,删除里面的过期键,至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。
    redis采用的是定期删除+惰性删除策略
    为什么不用定时删除策略?
    定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.
    定期删除+惰性删除是如何工作的呢
    定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
    于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
    采用定期删除+惰性删除就没其他问题了么?
    不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。

    redis的6种数据淘汰策略


    在redis.conf中有一行配置
    maxmemory-policy volatile-lru

    1.noeviction:不删除策略,达到最大内存限制时刻,如果需要更多内存,直接返回错误信息
    2.allkey-lru:所有key通用,优先删除最近最少使用的key(LRU)
    3.volatile-lru:只限于设置了expire的部分,优先删除最近最少使用的key
    4.allkey-random:所有key通用,随机删除一部分key
    5.volatile-random:只限于设置了expire部分,随即删除一部分key
    6.volatile-ttl:只设置了expire的部分;优先删除剩余时间短的key
    ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。

    Redis 持久化


    RDB:是Redis默认的持久化方式。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。( 快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品。)
    AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
    当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。

    缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级


    缓存雪崩:由于原有缓存失效,新缓存未到期间
    (例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
    解决办法:
    大多数系统设计者考虑用加锁( 最多的解决方案)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开。

    缓存穿透
    缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
    解决办法;
    最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
    另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。
    对于空间的利用到达了一种极致,那就是Bitmap和布隆过滤器(Bloom Filter)。
    Bitmap: 典型的就是哈希表
    缺点是,Bitmap对于每个元素只能记录1bit信息,如果还想完成额外的功能,恐怕只能靠牺牲更多的空间、时间来完成了。

    布隆过滤器(推荐)
    就是引入了k(k>1)k(k>1)个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。
    它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
    Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。
    Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个URL的值有可能相同。为了减少冲突,我们可以多引入几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。
    Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在。
    受提醒补充:缓存穿透与缓存击穿的区别
    缓存击穿:是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据。
    解决方案;在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。
    增:给一个我公司处理的案例:背景双机拿token,token在存一份到redis,保证系统在token过期时都只有一个线程去获取token;线上环境有两台机器,故使用分布式锁实现。

    缓存预热
    缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
    解决思路:
    1、直接写个缓存刷新页面,上线时手工操作下;
    2、数据量不大,可以在项目启动的时候自动进行加载;
    3、定时刷新缓存;

    缓存更新
    除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
    (1)定时去清理过期的缓存;
    (2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
    两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。
    缓存降级
    当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
    降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
    以参考日志级别设置预案:
    (1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
    (2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
    (3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
    (4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

    服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。

    Mysql和Redis的双写一致性


    由于缓存和数据库是分开的,无法做到原子性的同时进行数据修改,可能出现缓存更新失败,或者数据库更新失败的情况,这时候会出现数据不一致,影响前端业务
    • 先更新数据库,再更新缓存。缓存可能更新失败,读到老数据
    • 先删缓存,再更新数据库。并发时,读操作可能还是会将旧数据读回缓存
    • 先更新数据库,再删缓存。也存在缓存删除失败的可能

    最经典的缓存+数据库读写的模式,Cache Aside Pattern
    读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
    更新的时候,先更新数据库,然后再删除缓存。

    为什么是删除而不是更新?
    删除更加轻量,延迟加载的一种实现,更新可能涉及多个表、比较耗时
    延时双删:先删除缓存,再更新数据库,休眠1s、再次删除缓存。写数据的休眠时间则在读数据业务逻辑的耗时基础上,加几百ms即可。这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据,并发还是可能读到旧值覆盖缓存
    终极方案:
    将访问操作串行化
    1 .先删缓存,将更新数据库的操作放进有序队列中
    2 .从缓存查不到的查询操作,都进入有序队列
    会面临的问题:
    1 .读请求积压,大量超时,导致数据库的压力:限流、熔断
    2 .如何避免大量请求积压:将队列水平拆分,提高并行度。
    3 .保证相同请求路由正确。
    先写 MySQL,通过 Binlog,异步更新 Redis
    对于异地容灾、数据汇总等,建议会用这种方式,比如 binlog + kafka,数据的一致性也可以达到秒级;
    纯粹的高并发场景,不建议用这种方案,比如抢购、秒杀等。

    单线程redis为什么这么快


    (一)纯内存操作
    (二)单线程操作,避免了频繁的上下文切换
    (三)采用了非阻塞I/O多路复用机制

    Redis 为什么是单线程的


    因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了,Redis利用队列技术将并发访问变为串行访问
    1)绝大部分请求是纯粹的内存操作(非常快速)

    2)采用单线程,避免了不必要的上下文切换和竞争条件
    3)非阻塞IO优点:
    1.速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
    2. 支持丰富数据类型,支持string,list,set,sorted set,hash
    3.支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
    4. 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除如何解决redis的并发竞争key问题

    同时有多个子系统去set一个key。这个时候要注意什么呢? 不推荐使用redis的事务机制。因为我们的生产环境,基本都是redis集群环境,做了数据分片操作。你一个事务中有涉及到多个key操作的时候,这多个key不一定都存储在同一个redis-server上。因此,redis的事务机制,十分鸡肋。
    (1)如果对这个key操作,不要求顺序: 准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可
    (2)如果对这个key操作,要求顺序: 分布式锁+时间戳。 假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。
    (3) 利用队列,将set方法变成串行访问也可以redis遇到高并发,如果保证读写key的一致性
    对redis的操作都是具有原子性的,是线程安全的操作,你不用考虑并发问题,redis内部已经帮你处理好并发的问题了。

    Redis 内部结构


    dict 本质上是为了解决算法中的查找问题(Searching)是一个用于维护key和value映射关系的数据结构,与很多语言中的Map或dictionary类似。 本质上是为了解决算法中的查找问题(Searching)
    sds sds就等同于char * 它可以存储任意二进制数据,不能像C语言字符串那样以字符’\0’来标识字符串的结 束,因此它必然有个长度字段。
    skiplist (跳跃表) 跳表是一种实现起来很简单,单层多指针的链表,它查找效率很高,堪比优化过的二叉平衡树,且比平衡树的实现,
    quicklist
    ziplist 压缩表 ziplist是一个编码后的列表,是由一系列特殊编码的连续内存块组成的顺序型数据结构,

    Redis三种集群模式


    主从模式
    至少需要两台redis服务器,一台主节点(master)、一台从节点(slave),组成主从模式的Redis集群。通常来说,master主要负责写,slave主要负责读,主从模式实现了读写分离。

    在这里插入图片描述
    集群中有多台redis节点,就必须保证每个节点中的数据是一致的。redis中,为了保持数据一致性,数据总是从master复制到slave,这就是redis的主从复制。
    主从复制的作用:
    数据冗余:实现了数据的热备份,是持久化之外的另一种数据冗余方式
    故障恢复:master故障时,slave可以提供服务,实现故障快速恢复
    负载均衡:master负责写,slave负责读。在写少读多的场景下可以极大提高redis吞吐量
    高可用基石:主从复制是redis哨兵模式和集群模式的基础。
    主从复制实现原理:

    主从复制过程主要可以分为3个阶段:连接建立阶段、数据同步阶段、命令传播阶段。

    连接建立阶段:在主从节点之间建立连接,为数据同步做准备。
    数据同步阶段:执行数据的全量(或增量)复制(复制RDB文件)
    命令传播阶段:主节点将已执行的命令发送给从节点,从节点接收命令并执行,从而实现主从节点的数据一致性
    主从模式中,一个主节点可以有多个从节点。为了减少主从复制对主节点的性能影响,一个从节点可以作为另外一个从节点的主节点进行主从复制。

    不足之处:主节点宕机之后,需要手动拉起从节点来提供业务,不能达到高可用。

    哨兵模式
    Redis Sentinel是Redis的高可用实现方案,它可以实现对redis的监控、通知和自动故障转移,当redis master挂掉之后,可以自动拉起slave提供业务,从而实现redis的高可用。为了避免Sentinel本身出现单点故障,Sentinel自己也可采用集群模式。

    在这里插入图片描述

    哨兵模式的原理

    Sentinel是一种特殊的redis节点,每个sentinel节点会维护与其他redis节点(包括master/slave/sentinel)的心跳。
    当一个sentinel节点与master节点的心跳丢失时,这个sentinel节点就会认为master节点出现了故障,处于不可用的状态,这种判定叫作主观下线(即sentinel节点自己主观认为master下线了)
    之后,这个sentinel节点会与其他sentinel节点交换信息,如果发现认为主节点发生故障的sentinel节点的个数超过了某个阈值(通常为sentinel节点总数的1/2+1,即超过半数),则sentinel会认为master节点已经处于客观下线的状态,即大家都认为master故障不可用了。
    之后,sentinel节点中会选举处一个sentinel leader来执行redis主节点的故障转移。

    被选举出的 Sentinel 领导者进行故障转移的具体步骤如下:
    (1)在从节点列表中选出一个节点作为新的主节点
    过滤不健康或者不满足要求的节点;
    选择 slave-priority(优先级)最高的从节点, 如果存在则返回, 不存在则继续;
    选择复制偏移量最大的从节点 , 如果存在则返回, 不存在则继续;
    选择 runid 最小的从节点。
    (2)Sentinel 领导者节点会对选出来的从节点执行 slaveof no one 命令让其成为主节点。
    (3)Sentinel 领导者节点会向剩余的从节点发送命令,让他们从新的主节点上复制数据。
    (4)Sentinel 领导者会将原来的主节点更新为从节点, 并对其进行监控, 当其恢复后命令它去复制新的主节点。

    集群模式
    主从模式实现了数据的热备份,哨兵模式实现了redis的高可用。但是有一个问题,这两种模式都没有解决,这两种模式都只能有一个master节点负责写操作,在高并发的写操作场景,master节点就会成为性能瓶颈。
    redis的集群模式中可以实现多个节点同时提供写操作,redis集群模式采用无中心结构,每个节点都保存数据,节点之间互相连接从而知道整个集群状态。
    如图所示集群模式其实就是多个主从复制的结构组合起来的,每一个主从复制结构可以看成一个节点,那么上面的Cluster集群中就有三个节点。

    在这里插入图片描述
    Redis 集群模式的工作原理能说一下么?在集群模式下,Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?


    了解一致性 hash 算法吗?如何动态增加和删除一个节点?


    Redis 常见性能问题和解决方案?


    (1) Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件
    (2) 如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次
    (3) 为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内
    (4) 尽量避免在压力很大的主库上增加从库
    (5) 主从复制不要用图状结构,用单向链表结构更为稳定,即: Master <- Slave1 <- Slave2 <-Slave3…

    Redis的IO多路复用


    理解IO多路复用原理:
    我们拿到了一堆文件描述符fd(网络相关、磁盘文件相关等等,任何文件描述符都可以)
    通过调用某个函数告诉内核:“这个函数你先不要返回,你替我监视着这些描述符,当这堆文件描述符中有可以进行I/O读写操作的时候你再返回告诉我”
    当调用的这个函数返回后,我们就能知道哪些文件描述符可以进行I/O操作了。
    实现IO多路复用的三种机制
    在Linux世界中有这样三种机制可以用来进行I/O多路复用
    select
    poll
    epoll

    select
    在select这种I/O多路复用机制下,我们需要把想监控的fd集合通过函数参数的形式告诉select,然后select会将这些fd集合拷贝到内核中,然后遍历所有fd找到就绪fd返回给应用程序。

    select三个缺点:

    规定监控的fd集合不能超过1024个
    因为数据拷贝是有性能损耗的,当线程监控的fd过多会导致拷贝数据的时间过多,从而使得性能变慢。

    每次调用select,都需要把所有的fd集合从用户态拷贝到内核态,fd越多开销则越大;

    每次调用select都需要在内核遍历所有fd来找到具体是哪个文件描述符已就绪,这个开销在fd很多时也很大。

    poll
    poll是基于链表来存储的,所以poll没有最大连接数的限制,但是另外两个缺点没有解决

    epoll
    Epoll只关心就绪的连接,不关心连接总数,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll

    Epoll的优点
    文件描述符数量不再受限(1G的内存上能监听约10万个端口);解决缺点1
    使用内存映射mmap技术,节省了用户态和内核态间数据拷贝的资源消耗;解决缺点2
    不再遍历所有fd,只有就绪的fd才会执行回调函数。I/O的效率不会随着监视fd的数量的增长而下降;解决缺点3

    三种模式如何选择
    表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好
    但是当并发连接(fd)较多时,Epoll的优势便真正展现出来。

    Redis事务


    事务ACID
    事务是一系列严密的逻辑操作,可以使一条SQL语句也可以是一组,是恢复和并发控制的基本单元。
    ACID指一致性,原子性,隔离性,持久性。
    原子性:事务中的多个操作必须度完成,或者都不完成。
    一致性:数据库中的数据在事务执行前后状态是一致的。
    隔离性:数据库执行该事务的时候,不受其他事务影响。
    持久性:事务对数据的修改持久化保存。
    其中在MySQL中一致性是最终追求的目标,隔离性由锁机制实现,原子性和持久性由redolog和undolog保证。

    Redis如何实现事务
    Redis通过命令开启和提交事务,和命令队列的形式支持事务

    MULTI开启事务。
    提交redis数据操作命令到命令队列中,不会立即执行。
    EXEC提交事务,实际执行队列中的命令。
    Redis事务机制保证哪些属性
    原子性:

    第一种情况,执行EXEC命令前,客户端发送的命令本身有错误,比如语法问题或者本身命令不存在,这些命令入队就检测出来错误。后续即使添加正确命令,执行EXEC命令提交事务也会失败。保证原子性。
    第二种情况,错误命令入队redis未检查出来,比如命令和操作数据不符,这是会把正确命令执行完,错误命令无法执行,因而命令队列中的所有命令只执行了一部分事务无法保证原子性。同样在命令入队之后,如果发现命令有错,提供DISCARD命令可以主动放弃事务执行,清空命令队列。
    第三种情况,执行EXEC命令,redis故障,导致执行失败。如果开启了AOF日志记录命令,就可以恢复并不执行事务操作,保持原子性,如果未开启日志,则无法保证。
    也就是说:

    如果命令入队就报错,会放弃事务,保证原子性
    如果命令入队没报错,实际执行报错,不保证原子性
    EXEC命令执行时候故障,如果开启了AOF日志,则保证原子性
    一致性:

    命令入队就报错,事务会被放弃,保持一致性
    执行才报错,错误的命令不执行,正确的命令执行,也保证一致性
    执行EXEC命令redis发生故障。如果记录了日志,可以清除事务操作,如果没记录日志,那么就可以恢复到事务执行前的状态,也是保持一致性。
    隔离性
    分两种情况:

    并发操作在EXEC命令执行前,要使用WATCH机制保证隔离性,否则无法保证。
    并发操作在EXEC后,可以保证
    WATCH机制可以再事务执行前监控一个或者多个键的值变化情况,如果执行EXEC事务,WATCH机制会检查监控的键是否被其他客户端修改了,就放弃事务,避免被破坏隔离性。

    在这里插入图片描述
    如果没有WATCH机制,可能直接开启事务的命令读取的数据,在事务未提交之前,其他事务可能就修改该数据,提交事务读取的数据不一致了。

    持久性
    无论是什么持久化模式,由于是内存数据库,掉电宕机,都无法保证修改的数据化持久化属性。

    4、总结
    4个命令支持事务

    在这里插入图片描述
    redis事务机制可以保证一致性和隔离性,无法保证持久性,但是对于redis而言,本身是内存数据库,所以持久化不是必须属性。
    原子性需要自己进行检查,尽可能保证。

    redis批量处理数据


    管道(pipe)
    就是把n个命令通过一个pipe发送到服务器端,服务器端处理完成以后再返回一个响应结果。而一条一条set需要n次请求n次处理n次响应,而管道只要一次请求n次处理一次响应。由此可以见,pipe不是原子性的。

    1. JedisPool jedisPool = new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6379);
    2. Jedis jedis = jedisPool.getResource();
    3. long start = System.currentTimeMillis();
    4. Pipeline pipeline = jedis.pipelined();
    5. for (int i = 0; i < 1000000; i++) {
    6.     pipeline.set("Key" + i, "Value" + i);
    7. }
    8. pipeline.sync();
    9. long end = System.currentTimeMillis();
    10. System.out.println(end - start);


    优点:减少网络耗时。

    弊端:不是原子操作。需要适当去处理每个pipe传输命令的多少,因为会在回复这个pipe的返回值,会占有大量内存。

    通过pipe mode模式
    这种方式需要手动生成Redis命令集合。然后使用cat data.txt | redis-cli --pipe 命令在客户端命令行执行。
    测试脚本:

    1. #! /bin/bash/
    2. for((i=1;i<=1000000;i++))
    3.     do
    4.     echo "SET key${i} value${i}" >> redis.txt 
    5. done
    6. startTime=`date +'%s'`
    7. cat redis.txt | redis-cli --pipe -a password
    8. endTime=`date +'%s'`
    9. echo ${endTime}
    10. echo `expr ${endTime} - ${startTime}` >>time


    优点:很快。通过上面两个执行100w条命令,pipe-mode执行大约是3s,pipe命令是5.4s。(官网)大量插入数据的同时又需要执行其他新命令时,这时读取数据的同时需要确保请可能快的的写入数据。
    缺点:可能需要手动生成命令集合的文本。
     

  • 相关阅读:
    阿里P8熬了一个月肝出这份32W字Java面试手册,在Github标星31K+
    Kafka-Java一:Spring实现kafka消息的简单发送
    【案例分享】BenchmarkSQL 5.0 压测 openGauss 5.0.0
    国内外的免费AI作图工具
    CTF—Go题目复现
    java-php-python-ssm新冠病毒感染者档案信息管理系统计算机毕业设计
    今天写了一个可以测试并发数和运行次数的压力测试代码。(Java)
    试从多个方面比较电路交换、报文交换和分组交换的主要优缺点?
    一图胜千言,200行代码图解Python Matplotlib (面向对象法)!
    基于SSM的视频播放系统的设计与实现
  • 原文地址:https://blog.csdn.net/javajy/article/details/127851568