如何进行Redis深度分析
发表于:2025-12-03 作者:千家信息网编辑
千家信息网最后更新 2025年12月03日,今天就跟大家聊聊有关如何进行Redis深度分析,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。0、基础:万丈高楼平地起1) 当字符串长度小于1M
千家信息网最后更新 2025年12月03日如何进行Redis深度分析
今天就跟大家聊聊有关如何进行Redis深度分析,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
0、基础:万丈高楼平地起
1) 当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为512M。2) 如果value值是一个整数,还可以对它进行自增操作。自增是有范围的,它的范围是signed long的最大最小值,超过了这个值,Redis会报错,最大值 9223372036854775807 3) Redis的列表相当于Java语言里面的LinkedList,注意它是链表而不是数组。Redis的列表结构常用来做异步队列使用。将需要延后处理的任务结构体序列化成字符串塞进Redis的列表,另一个线程从这个列表中轮询数据进行处理。 右边进左边出--> 队列 右边进右边出--> 栈 如果再深入一点,你会发现 Redis 底层存储的还不是一个简单的linkedlist,而是称之为快速链表quicklist的一个结构。首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的时候才会改成quicklist。因为普通的链表需要的附加指针空间太大,会比较浪费空间,而且会加重内存的碎片化。比如这个列表里存的只是 int 类型的数据,结构上还需要两个额外的指针prev和next。所以Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。 4) 压缩列表ziplist是一种为节约内存而开发的顺序型数据结构,它被用作列表键和哈希键的底层实现之一。5) Hash,Redis的字典的值只能是字符串,而且Redis采用渐进式Rehash策略6) set,Redis的集合相当于Java语言里面的HashSet,内部的键值对是无序的唯一的,其内部实现相当于一个特殊的字典,字典中所有的value都是一个值NULL。set结构可以用来存储活动中奖的用户ID,因为有去重功能,可以保证同一个用户不会中奖两次。 7) zset,它类似于Java的SortedSet和HashMap的结合体,一方面它是一个set,保证了内部value的唯一性,另一方面它可以给每个value赋予一个score,代表这个value的排序权重。它的内部实现用的是一种叫着跳跃列表的数据结构。zset还可以用来存储学生的成绩,value值是学生的ID,score是他的考试成绩。我们可以对成绩按分数进行排序就可以得到他的名次。zset可以用来存粉丝列表,value值是粉丝的用户ID,score是关注时间。我们可以对粉丝列表按关注时间进行排序。
//https://yq.aliyun.com/articles/666398typedef struct zset { // 字典,键为成员,值为分值 // 用于支持 O(1) 复杂度的按成员取分值操作 dict *dict; // 跳跃表,按分值排序成员 // 用于支持平均复杂度为 O(log N) 的按分值定位成员操作 // 以及范围操作 zskiplist *zsl;} zset;1、千帆竞发:Redis分布式锁
1) 使用setnx命令实现分布式锁 超时问题? Redis 的分布式锁不能解决超时问题,如果在加锁和释放锁之间的逻辑执行的太长,以至于超出了锁的超时限制,就会出现问题。因为这时候锁过期了,第二个线程重新持有了这把锁,但是紧接着第一个线程执行完了业务逻辑,就把锁给释放了,第三个线程就会在第二个线程逻辑执行完之间拿到了锁。 解决办法:1.1) Redis分布式锁不要用于较长时间任务。如果真的偶尔出现了,数据出现的小波错乱可能需要人工介入解决。1.2) 有一个更加安全的方案是为set指令的value参数设置为一个随机数,释放锁时先匹配随机数是否一致,然后再删除key。但是匹配value和删除key不是一个原子操作,这就需要使用Lua脚本来处理了,因为Lua脚本可以保证连续多个指令的原子性执行。2) 单机Redis实现分布式锁的缺陷 比如在Sentinel集群中,主节点挂掉时,从节点会取而代之,客户端上却并没有明显感知。原先第一个客户端在主节点中申请成功了一把锁,但是这把锁还没有来得及同步到从节点,主节点突然挂掉了。然后从节点变成了主节点,这个新的节点内部没有这个锁,所以当另一个客户端过来请求加锁时,立即就批准了。这样就会导致系统中同样一把锁被两个客户端同时持有,不安全性由此产生。不过这种不安全也仅仅是在主从发生failover的情况下才会产生,而且持续时间极短,业务系统多数情况下可以容忍。 解决办法:RedLock 加锁时,它会向过半节点发送 set(key, value, nx=True, ex=xxx) 指令,只要过半节点set成功,那就认为加锁成功。释放锁时,需要向所有节点发送del指令。不过Redlock算法还需要考虑出错重试、时钟漂移等很多细节问题,同时因为Redlock需要向多个节点进行读写,意味着相比单实例 Redis 性能会下降一些。如果你很在乎高可用性,希望挂了一台redis完全不受影响,那就应该考虑 redlock。不过代价也是有的,需要更多的 redis 实例,性能也下降了,代码上还需要引入额外的library,运维上也需要特殊对待,这些都是需要考虑的成本,使用前请再三斟酌。 3) 锁冲突处理 我们讲了分布式锁的问题,但是没有提到客户端在处理请求时加锁没加成功怎么办。 一般有3种策略来处理加锁失败:3.1) 直接抛出异常,通知用户稍后重试 这种方式比较适合由用户直接发起的请求,用户看到错误对话框后,会先阅读对话框的内容,再点击重试,这样就可以起到人工延时的效果。如果考虑到用户体验,可以由前端的代码替代用户自己来进行延时重试控制。它本质上是对当前请求的放弃,由用户决定是否重新发起新的请求。 3.2) sleep一会再重试,不推荐3.3) 将请求转移至延时队列,过一会再试 这种方式比较适合异步消息处理,将当前冲突的请求扔到另一个队列延后处理以避开冲突。
2、缓兵之计:延时队列
1)异步消息队列 Redis的消息队列不是专业的消息队列,它没有非常多的高级特性,没有ack保证,如果对消息的可靠性有着极致的追求,那么它就不适合使用。 Redis的list(列表)数据结构常用来作为异步消息队列使用,使用rpush/lpush操作入队列,使用lpop和rpop来出队列。 队列空了怎么办? 可是如果队列空了,客户端就会陷入pop的死循环,不停地pop,没有数据,接着再pop,又没有数据。这就是浪费生命的空轮询。空轮询不但拉高了客户端的CPU,redis的QPS也会被拉高,如果这样空轮询的客户端有几十来个,Redis的慢查询可能会显著增多。 解决办法:1.1) 使用sleep来解决这个问题,让线程睡一会2.1) 使用blpop/brpop,阻塞读在队列没有数据的时候,会立即进入休眠状态,一旦数据到来,则立刻醒过来。消息的延迟几乎为零。空闲连接自动断开怎么办?解决办法: 如果线程一直阻塞在哪里,Redis的客户端连接就成了闲置连接,闲置过久,服务器一般会主动断开连接,减少闲置资源占用。这个时候blpop/brpop会抛出异常来。所以编写客户端消费者的时候要小心,注意捕获异常,还要重试。 2) 延时队列 延时队列可以通过Redis的zset(有序列表)来实现。我们将消息序列化成一个字符串作为zset的value,这个消息的处理时间作为score,然后用多个线程轮询zset获取到期的任务进行处理,多个线程是为了保障可用性,万一挂了一个线程还有其它线程可以继续处理。因为有多个线程,所以需要考虑并发争抢任务,确保任务不能被多次执行。
//Redis实现延迟队列public class RedisDelayingQueue{ static class TaskItem { public String id; public T msg; } private Type TaskType = new TypeReference >(){}.getType(); private Jedis jedis; private String queueKey; public RedisDelayingQueue(Jedis jedis, String queueKey){ this.jedis = jedis; this.queueKey = queueKey; } public void delay(T msg){ TaskItem taskItem = new TaskItem(); taskItem.id = UUID.randomUUID().toString(); taskItem.msg = msg; String s = JSON.toJSONString(taskItem); jedis.zadd(queueKey, System.currentTimeMillis() + 5000, s); } public void loop(){ while (!Thread.interrupted()){ //只取一条数据 Set values = jedis.zrangeByScore(queueKey,0,System.currentTimeMillis(),0,1); if(values.isEmpty()){ try { Thread.sleep(500); } catch (InterruptedException e) { break; } continue; } String s = (String) values.iterator().next(); //zrem用于移除有序集中一个或多个成员 if(jedis.zrem(queueKey, s) > 0){ TaskItem task = JSON.parseObject(s, TaskType); this.handleMsg(task.msg); } } } private void handleMsg(Object msg) { System.out.println(msg); } public static void main(String[] args) { Jedis jedis = new Jedis(); RedisDelayingQueue queue = new RedisDelayingQueue(jedis, "test-queue"); Thread producer = new Thread(){ @Override public void run() { for (int i = 0; i < 10; i++){ queue.delay("lwh" + i); } } }; Thread consumer = new Thread(){ @Override public void run() { queue.loop(); } }; producer.start(); consumer.start(); try { producer.join(); Thread.sleep(6000); consumer.interrupt(); consumer.join(); } catch (InterruptedException e) { e.printStackTrace(); } }}
3、节衣缩食: 位图
1) 位图 位图不是特殊的数据结构,它的内容其实就是普通的字符串,也就是 byte 数组。我们可以使用普通的 get/set 直接获取和设置整个位图的内容,也可以使用位图操作 getbit/setbit等将 byte 数组看成「位数组」来处理。 redis位图可以实现零存整取、零存零取等。「零存」就是使用 setbit 对位值进行逐个设置,「整存」就是使用字符串一次性填充所有位数组,覆盖掉旧值。 命令形如 1) setbit s 1 1 2) getbit s 3) get s 4) set s h 2) 统计和查找 Redis提供了位图统计指令bitcount和位图查找指令bitpos,bitcount用来统计指定位置范围内1的个数,bitpos用来查找指定范围内出现的第一个0或1。比如我们可以通过bitcount统计用户一共签到了多少天,通过bitpos指令查找用户从哪一天开始第一次签到。如果指定了范围参数[start,end],就可以统计在某个时间范围内用户签到了多少天,用户自某天以后的哪天开始签到。 3) bitfield
4、四两拨千斤: HyperLogLog
1) HyperLogLog使用 统计PV:每个网页一个独立的Redis计数器 统计UV?解决办法:1.1) set去重,页面访问量大的情况下,耗费太多存储空间1.2) 使用HyperLogLog,不精确去重,标准误差0.81% HyperLogLog提供了两个指令pfadd和pfcount,根据字面意义很好理解,一个是增加计数,一个是获取计数。pfadd用法和set集合的sadd是一样的,来一个用户ID,就将用户ID塞进去就是。pfcount和scard用法是一样的,直接获取计数值。 pfadd test-log user1 pfadd test-log user2 pfcount test-log HyperLogLog 除了上面的pfadd和pfcount之外,还提供了第三个指令pfmerge,用于将多个pf计数值累加在一起形成一个新的pf值。比如在网站中我们有两个内容差不多的页面,运营说需要这两个页面的数据进行合并。其中页面的UV访问量也需要合并,那这个时候pfmerge就可以派上用场了。
5、层峦叠嶂:布隆过滤器
1) 布隆过滤器 讲个使用场景,比如我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。 基本指令:布隆过滤器有二个基本指令,bf.add添加元素,bf.exists查询元素是否存在,它的用法和set集合的sadd和sismember差不多。注意bf.add只能一次添加一个元素,如果想要一次添加多个,就需要用到bf.madd指令。同样如果需要一次查询多个元素是否存在,就需要用到bf.mexists指令。 bf.add test-filter user1 bf.add test-filter user2 bf.exists test-filter user1 Redis其实还提供了自定义参数的布隆过滤器,需要我们在add之前使用bf.reserve指令显式创建。如果对应的 key已经存在,bf.reserve会报错。bf.reserve有三个参数,分别是key,error_rate和initial_size。错误率越低,需要的空间越大。initial_size参数表示预计放入的元素数量,当实际数量超出这个数值时,误判率会上升。2) 布隆过滤器的原理 每个布隆过滤器对应到Redis的数据结构里面就是一个大型的位数组和几个不一样的无偏hash函数。所谓无偏就是能够把元素的hash值算得比较均匀。向布隆过滤器中添加key时,会使用多个hash函数对 key进行hash算得一个整数索引值然后对位数组长度进行取模运算得到一个位置,每个hash函数都会算得一个不同的位置。再把位数组的这几个位置都置为1就完成了add操作。3) 布隆过滤器的其他应用 在爬虫系统中,我们需要对URL进行去重,已经爬过的网页就可以不用爬了。但是URL太多了,几千万几个亿,如果用一个集合装下这些URL地址那是非常浪费空间的。这时候就可以考虑使用布隆过滤器。它可以大幅降低去重存储消耗,只不过也会使得爬虫系统错过少量的页面。布隆过滤器在NoSQL数据库领域使用非常广泛,我们平时用到的HBase、Cassandra还有LevelDB、RocksDB内部都有布隆过滤器结构,布隆过滤器可以显著降低数据库的IO请求数量。当用户来查询某个row时,可以先通过内存中的布隆过滤器过滤掉大量不存在的row请求,然后再去磁盘进行查询。 邮箱系统的垃圾邮件过滤功能也普遍用到了布隆过滤器,因为用了这个过滤器,所以平时也会遇到某些正常的邮件被放进了垃圾邮件目录中,这个就是误判所致,概率很低。
6、断尾求生:简单限流
除了控制流量,限流还有一个应用目的是用于控制用户行为,避免垃圾请求。比如在UGC社区,用户的发帖、回复、点赞等行为都要严格受控,一般要严格限定某行为在规定时间内允许的次数,超过了次数那就是非法行为。对非法行为,业务必须规定适当的惩处策略。

//这个限流需求中存在一个滑动时间窗口,想想zset数据结构的score值,是不是可以通过score来圈出这个时间窗口来。而且我们只需要//保留这个时间窗口,窗口之外的数据都可以砍掉。那这个zset的value填什么比较合适呢?它只需要保证唯一性即可,用uuid会比较浪费//空间,那就改用毫秒时间戳吧。//但这种方案也有缺点,因为它要记录时间窗口内所有的行为记录,如果这个量很大,比如限定60s内操作不得超过100w次这样的参数,它//是不适合做这样的限流的,因为会消耗大量的存储空间。public class SimpleRateLimiter { private final Jedis jedis; public SimpleRateLimiter(Jedis jedis){ this.jedis = jedis; } public boolean isActionAllowed(String userId, String actionKey, int period, int maxCount) throws IOException { String key = String.format("hist:%s:%s", userId, actionKey); long nowTs = System.currentTimeMillis(); Pipeline pipeline = jedis.pipelined(); //开启一个事务 pipeline.multi(); //value和score都用毫秒时间戳 pipeline.zadd(key, nowTs, "" + nowTs); //移除时间窗口之外的行为记录,剩下的都是时间窗口内的 pipeline.zremrangeByScore(key, 0, nowTs - period * 1000); //获得[nowTs - period * 1000, nowTs]的key的数量 Response count = pipeline.zcard(key); //每次设置都更新key的过期时间 pipeline.expire(key, period); //在事务中执行上述命令 pipeline.exec(); pipeline.close(); return count.get() <= maxCount; } public static void main(String[] args) throws IOException, InterruptedException { Jedis jedis=new Jedis("localhost",6379); SimpleRateLimiter limiter=new SimpleRateLimiter(jedis); for (int i = 0; i < 20; i++) { //每个用户在1秒内最多能做五次动作 System.out.println(limiter.isActionAllowed("lwh","reply",1,5)); } }} 7、一毛不拔:漏斗限流
Redis4.0提供了一个限流Redis模块,它叫redis-cell。该模块也使用了漏斗算法,并提供了原子的限流指令。有了这个模块,限流问题就非常简单了。 cl.throttle lwh:reply 15 30 60 1 上面这个指令的意思是允许「用户lwh回复行为」的频率为每 60s 最多 30 次(漏水速率),漏斗的初始容量为 15,也就是说一开始可以连续回复 15 个帖子,然后才开始受漏水速率的影响。
8、近水楼台:GeoHash
Redis在3.2版本以后增加了地理位置GEO模块,意味着我们可以使用Redis来实现摩拜单车「附近的 Mobike」、美团和饿了么「附近的餐馆」这样的功能了。 业界比较通用的地理位置距离排序算法是GeoHash算法,Redis也使用GeoHash算法。GeoHash算法将二维的经纬度数据映射到一维的整数,这样所有的元素都将在挂载到一条线上,距离靠近的二维坐标映射到一维后的点之间距离也会很接近。当我们想要计算「附近的人时」,首先将目标位置映射到这条线上,然后在这个一维的线上获取附近的点就行了。 在Redis里面,经纬度使用52位的整数进行编码,放进了zset里面,zset的value是元素的key,score是GeoHash的52位整数值。zset的score虽然是浮点数,但是对于52位的整数值,它可以无损存储。在使用Redis进行Geo查询时,我们要时刻想到它的内部结构实际上只是一个zset(skiplist)。通过zset的score 排序就可以得到坐标附近的其它元素 (实际情况要复杂一些,不过这样理解足够了),通过将score还原成坐标值就可以得到元素的原始坐标。 1) 添加,geoadd指令携带集合名称以及多个经纬度名称三元组 geoadd company 116.48105 39.996794 juejin geoadd company 116.514203 39.905409 ireader 2) 距离,geodist指令可以用来计算两个元素之间的距离 geodist company juejin ireader km 3) 获取元素位置,geopos指令可以获取集合中任意元素的经纬度坐标 geopos company juejin 我们观察到获取的经纬度坐标和geoadd进去的坐标有轻微的误差,原因是geohash对二维坐标进行的一维映射是有损的,通过映射再还原回来的值会出现较小的差别。对于「附近的人」这种功能来说,这点误差根本不是事。 4) 附近的公司,georadiusbymember指令是最为关键的指令,它可以用来查询指定元素附近的其它元素 //范围20公里以内最多3个元素按距离正排,它不会排除自身 georadiusbymember company ireader 20 km count 3 asc //三个可选参数 withcoord withdist withhash 用来携带附加参数 //withdist可以显示距离 //withcoord显示坐标 georadiusbymember company ireader 20 km withcoord withdist withhash count 3 asc 5) Redis还提供了根据坐标值来查询附近的元素,这个指令更加有用,它可以根据用户的定位来计算「附近的车」,「附近的餐馆」等。它的参数和georadiusbymember基本一致,除了将目标元素改成经纬度坐标值。 georadius company 116.514202 39.905409 20 km withdist count 3 asc 注意事项: 在一个地图应用中,车的数据、餐馆的数据、人的数据可能会有百万千万条,如果使用Redis的Geo数据结构,它们将全部放在一个zset集合中。在Redis的集群环境中,集合可能会从一个节点迁移到另一个节点,如果单个key的数据过大,会对集群的迁移工作造成较大的影响,在集群环境中单个key对应的数据量不宜超过1M,否则会导致集群迁移出现卡顿现象,影响线上服务的正常运行。 所以,这里建议Geo的数据使用单独的Redis实例部署,不使用集群环境。如果数据量过亿甚至更大,就需要对Geo数据进行拆分,按国家拆分、按省拆分,按市拆分,在人口特大城市甚至可以按区拆分。这样就可以显著降低单个zset集合的大小。
9、大海捞针:Scan
在平时线上Redis维护工作中,有时候需要从Redis实例成千上万的key中找出特定前缀的key列表来手动处理数据,可能是修改它的值,也可能是删除key。这里就有一个问题,如何从海量的key中找出满足特定前缀的key列表来? Redis提供了一个简单暴力的指令keys用来列出所有满足特定正则字符串规则的key。缺点:没有offset、limit参数,事件复杂度O(n),key过多时会导致卡顿 Redis为了解决这个问题,它在2.8版本中加入了大海捞针的指令--scan。scan相比keys具备有以下特点:1)、复杂度虽然也是 O(n),但是它是通过游标分步进行的,不会阻塞线程2)、提供limit参数,可以控制每次返回结果的最大条数,limit只是一个hint,返回的结果可多可少3)、同keys一样,它也提供模式匹配功能4)、服务器不需要为游标保存状态,游标的唯一状态就是scan返回给客户端的游标整数5)、返回的结果可能会有重复,需要客户端去重复,这点非常重要6)、遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的7)、单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零 scan提供了三个参数,第一个是cursor整数值,第二个是key的正则模式,第三个是遍历的limit hint。第一次遍历时,cursor值为0,然后将返回结果中第一个整数值作为下一次遍历的cursor。一直遍历到返回的cursor值为0时结束。 scan 0 match key99* count 1000 --> 返回cursor13796作为下次遍历的cursor scan 13976 match key99* count 1000 scan 指令返回的游标就是第一维数组的位置索引,我们将这个位置索引称为槽 (slot)。如果不考虑字典的扩容缩容,直接按数组下标挨个遍历就行了。limit参数就表示需要遍历的槽位数,之所以返回的结果可能多可能少,是因为不是所有的槽位上都会挂接链表,有些槽位可能是空的,还有些槽位上挂接的链表上的元素可能会有多个。每一次遍历都会将limit数量的槽位上挂接的所有链表元素进行模式匹配过滤后,一次性返回给客户端。 scan 的遍历顺序非常特别。它不是从第一维数组的第0位一直遍历到末尾,而是采用了高位进位加法来遍历。之所以使用这样特殊的方式进行遍历,是考虑到字典的扩容和缩容时避免槽位的遍历重复和遗漏。 在平时的业务开发中,要尽量避免大key的产生。有时候会因为业务人员使用不当,在Redis实例中会形成很大的对象,比如一个很大的hash,一个很大的zset这都是经常出现的。这样的对象对Redis的集群数据迁移带来了很大的问题,因为在集群环境下,如果某个key太大,会数据导致迁移卡顿。另外在内存分配上,如果一个key太大,那么当它需要扩容时,会一次性申请更大的一块内存,这也会导致卡顿。如果这个大key被删除,内存会一次性回收,卡顿现象会再一次产生。 不过Redis官方已经在redis-cli指令中提供了大key扫描功能。第二条指令每隔 100 条 scan 指令就会休眠 0.1s,ops 就不会剧烈抬升,但是扫描的时间会变长。 redis-cli -h 127.0.0.1 -p 7001 --bigkeys redis-cli -h 127.0.0.1 -p 7001 --bigkeys -i 0.1
10、鞭辟入里:线程IO模型
1) 定时任务 服务器处理要响应IO事件外,还要处理其它事情。比如定时任务就是非常重要的一件事。如果线程阻塞在select系统调用上,定时任务将无法得到准时调度。那Redis是如何解决这个问题的呢? Redis的定时任务会记录在一个称为最小堆的数据结构中。这个堆中,最快要执行的任务排在堆的最上方。在每个循环周期,Redis都会将最小堆里面已经到点的任务立即进行处理。处理完毕后,将最快要执行的任务还需要的时间记录下来,这个时间就是select系统调用的timeout参数。因为Redis知道未来timeout时间内,没有其它定时任务需要处理,所以可以安心睡眠timeout的时间。 Nginx 和 Node 的事件处理原理和 Redis 也是类似的
11、交头接耳:通信协议
Redis的作者认为数据库系统的瓶颈一般不在于网络流量,而是数据库自身内部逻辑处理上。所以即使Redis使用了浪费流量的文本协议,依然可以取得极高的访问性能。 RESP(Redis Serialization Protocol). RESP是Redis序列化协议的简写。它是一种直观的文本协议,优势在于实现异常简单,解析性能极好。
12、未雨绸缪:持久化
当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据。子进程因为数据没有变化,它能看到的内存里的数据在进程产生的一瞬间就凝固了,再也不会改变,这也是为什么 Redis 的持久化叫「快照」的原因。接下来子进程就可以非常安心的遍历数据了进行序列化写磁盘了。 Redis4.0混合持久化 重启Redis时,我们很少使用rdb来恢复内存状态,因为会丢失大量数据。我们通常使用AOF日志重放,但是重放AOF日志性能相对rdb来说要慢很多,这样在Redis实例很大的情况下,启动需要花费很长的时间。Redis4.0为了解决这个问题,带来了一个新的持久化选项--混合持久化。将rdb文件的内容和增量的AOF日志文件存在一起。这里的AOF日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量AOF日志,通常这部分AOF日志很小。 于是在Redis重启的时候,可以先加载rdb的内容,然后再重放增量AOF日志就可以完全替代之前的AOF全量文件重放,重启效率因此大幅得到提升。
13、开源节流:小对象压缩
1) 小对象压缩 如果Redis内部管理的集合数据结构很小,它会使用紧凑存储形式压缩存储。如果它存储的是hash结构,那么key和 value会作为两个entry相邻存在一起。如果它存储的是zset,那么value和score会作为两个entry相邻存在一起。 存储界限 当集合对象的元素不断增加,或者某个value值过大,这种小对象存储也会被升级为标准结构。 2) 内存回收机制 Redis并不总是可以将空闲内存立即归还给操作系统。 如果当前Redis内存有10G,当你删除了1GB的key后,再去观察内存,你会发现内存变化不会太大。原因是操作系统回收内存是以页为单位,如果这个页上只要有一个key还在使用,那么它就不能被回收。Redis虽然删除了1GB的key,但是这些key分散到了很多页面中,每个页面都还有其它key存在,这就导致了内存不会立即被回收。 不过,如果你执行flushdb,然后再观察内存会发现内存确实被回收了。原因是所有的key都干掉了,大部分之前使用的页面都完全干净了,会立即被操作系统回收。 Redis虽然无法保证立即回收已经删除的key的内存,但是它会重用那些尚未回收的空闲内存。这就好比电影院里虽然人走了,但是座位还在,下一波观众来了,直接坐就行。而操作系统回收内存就好比把座位都给搬走了。这个比喻是不是很6?
14、有备无患:主从同步
1) CAP理论 C:Consistent,一致性 A:Availbility,可用性 P:Partition tolerance,分区容忍性 分布式系统的节点往往都是分布在不同的机器上进行网络隔离开的,这意味着必然会有网络断开的风险,这个网络断开的场景的专业词汇叫着「网络分区」。 在网络分区发生时,两个分布式节点之间无法进行通信,我们对一个节点进行的修改操作将无法同步到另外一个节点,所以数据的「一致性」将无法满足,因为两个分布式节点的数据不再保持一致。除非我们牺牲「可用性」,也就是暂停分布式节点服务,在网络分区发生时,不再提供修改数据的功能,直到网络状况完全恢复正常再继续对外提供服务。 一句话概括CAP原理就是--网络分区发生时,一致性和可用性两难全。
看完上述内容,你们对如何进行Redis深度分析有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。
数据
指令
元素
内存
时间
用户
节点
处理
结构
过滤器
队列
客户
线程
布隆
存储
参数
就是
系统
任务
多个
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
数据库 第六章答案
网易版本超好玩的服务器
数据库一对一怎么变成一对多
数据库startup
mysql数据库更新命令行
全国列车时刻表数据库
2021网络技术博览会
数据库删除表格数据内容
软件开发实习生主要干嘛
16核cpu的服务器价格
服务器 网站
软件开发代码分为php和
管理员服务器上线时间
武汉恒通网络技术有限公司
数据库设计及工具
服务器散热片要求
江苏特种网络技术服务标准
为什么选择达梦数据库
直接访问远程数据库
软件开发 知名网站
餐饮软件开发正式免费版
20nt网络安全
学习计算机软件开发要多久
德国最大论文数据库
有什么好的网络安全视频
网络安全编程有哪些特点
西工大网络安全学院在哪个校区
高中生网络安全小品
中职网络技术教学视频
软件开发计划.xls