这个问题不能直接答是单线程还是多线程可以分redis的版本去回答2.612年10月的版本redis开始支持lua脚本3.X15年4月的版本redis支持集群是单线程的4.X17年7月的版本严格来说不是单线程负责处理客户端的线程是单线程删除使用多线程异步删除从而引入了多线程5.X18年10月的版本redis代码重构6.X20年5月的版本告别大家印象中的单线程用一种全新的多线程解决问题
正常情况下使用del指令可以很快的删除数据,而当被删除的key是一个非常大的对象时,例如时包含了成千上万个元素的hash集合时,那么del指令就会造成Redis主线程卡顿。这就是redis3.x单线程时代最经典的故障,大key删除的头疼问题,由于redis是单线程的,delbigKey.....等待很久这个线程才会释放,类似加了一个synchronized锁,你可以想象高并发下,程序堵成什么样子?
Redis将所有数据放在内存中,内存的响应时长大约为100纳秒,对于小数据包,Redis服务器可以处理8W到10W的QPS,这也是Redis处理的极限了,对于80%的公司来说,单线程的Redis已经足够使用了。
在Redis6.0中,多线程机制默认是关闭的,如果需要使用多线程功能,需要在redis.conf中完成两个设置1.设置io-thread-do-reads配置项为yes,表示启动多线程2.设置线程个数。关于线程数的设置,官方的建议是如果为4核的CPU,建议线程数设置为2或3,如果为8核CPU建议线程数设置为6,线程数一定要小于机器核数,线程数并不是越大越好。
stringhashlistsetzsetbitmaphyperlogloggeo
购物车
新增商品→hsetshopcar:uid10243344881新增商品→hsetshopcar:uid10243344771增加商品数量→hincrbyshopcar:uid10243344771商品总数→hlenshopcar:uid1024全部选择→hgetallshopcar:uid102
抽奖小程序
用户ID,立即参与按钮saddkey用户ID显示已经有多少人参与了,上图23208人参加SCARDkey抽奖(从set中任意选取N个中奖人)SRANDMEMBERkey2随机抽奖2个人,元素不删除SPOPkey3随机抽奖3个人,元素会删除
根据商品销售对商品进行排序显示
美团地图位置附近的酒店推送
备注:
有,是可能有
无,是肯定无
可以保证的是,如果布隆过滤器判断一个元素不在一个集合中,那这个元素一定不会在集合中
缓存穿透是什么
一般情况下,先查询缓存redis是否有该条数据,缓存中没有时,再查询数据库。
当数据库也不存在该条数据时,每次查询都要访问数据库,这就是缓存穿透。
缓存透带来的问题是,当有大量请求查询数据库不存在的数据时,就会给数据库带来压力,甚至会拖垮数据库。
可以使用布隆过滤器解决缓存穿透的问题
把已存在数据的key存在布隆过滤器中,相当于redis前面挡着一个布隆过滤器。
当有新的请求时,先到布隆过滤器中查询是否存在:
如果布隆过滤器中不存在该条数据则直接返回;
如果布隆过滤器中已存在,才去查询缓存redis,如果redis里没查询到则穿透到Mysql数据库
假设黑名单的数量是数以亿计的,存放起来就是非常耗费存储空间的,布隆过滤器则是一个较好的解决方案。
把所有黑名单都放在布隆过滤器中,在收到邮件时,判断邮件地址是否在布隆过滤器中即可。
哈希函数的概念是:将任意大小的输入数据转换成特定大小的输出数据的函数,转换后的数据称为哈希值或哈希编码,也叫散列值
如果两个散列值是不相同的(根据同一函数)那么这两个散列值的原始输入也是不相同的。
这个特性是散列函数具有确定性的结果,具有这种性质的散列函数称为单向散列函数。
散列函数的输入和输出不是唯一对应关系的,如果两个散列值相同,两个输入值很可能是相同的,但也可能不同,
这种情况称为“散列碰撞(collision)”。
用hash表存储大数据量时,空间效率还是很低,当只有一个hash函数时,还很容易发生哈希碰撞。
布隆过滤器(BloomFilter)是一种专门用来解决去重问题的高级数据结构。
实质就是一个大型位数组和几个不同的无偏hash函数(无偏表示分布均匀)。由一个初值都为零的bit数组和多个个哈希函数构成,用来快速判断某个数据是否存在。但是跟HyperLogLog一样,它也一样有那么一点点不精确,也存在一定的误判概率
添加key时
使用多个hash函数对key进行hash运算得到一个整数索引值,对位数组长度进行取模运算得到一个位置,
每个hash函数都会得到一个不同的位置,将这几个位置都置1就完成了add操作。
查询key时
只要有其中一位是零就表示这个key不存在,但如果都是1,则不一定存在对应的key。
结论:
当有变量被加入集合时,通过N个映射函数将这个变量映射成位图中的N个点,
把它们置为1(假定有两个变量都通过3个映射函数)。
查询某个变量的时候我们只要看看这些点是不是都是1,就可以大概率知道集合中有没有它了
如果这些点,有任何一个为零则被查询变量一定不在,
如果都是1,则被查询变量很可能存在,
为什么说是可能存在,而不是一定存在呢?那是因为映射函数本身就是散列函数,散列函数是会有碰撞的。
向布隆过滤器查询某个key是否存在时,先把这个key通过相同的多个hash函数进行运算,查看对应的位置是否都为1,
只要有一个位为0,那么说明布隆过滤器中这个key不存在;
如果这几个位置全都是1,那么说明极有可能存在;
因为这些位置的1可能是因为其他的key存在导致的,也就是前面说过的hash冲突。。。。。
就比如我们在add了字符串wmyskxz数据之后,很明显下面1/3/5这几个位置的1是因为第一次添加的wmyskxz而导致的;
此时我们查询一个没添加过的不存在的字符串inexistent-key,它有可能计算后坑位也是1/3/5,这就是误判了......
布隆过滤器的误判是指多个输入经过哈希之后在相同的bit位置1了,这样就无法判断究竟是哪个输入产生的,
因此误判的根源在于相同的bit位被多次映射且置1。
这种情况也造成了布隆过滤器的删除问题,因为布隆过滤器的每一个bit并不是独占的,很有可能多个元素共享了某一位。
如果我们直接删除这一位的话,会影响其他的元素
特性
一个元素判断结果为没有时则一定没有,
如果判断结果为存在的时候元素不一定存在。
布隆过滤器可以添加元素,但是不能删除元素。因为删掉元素会导致误判率增加。
为了解决布隆过滤器不能删除元素的问题,布谷鸟过滤器横空出世。论文《CuckooFilter:BetterThanBloom》
作者将布谷鸟过滤器和布隆过滤器进行了深入的对比。相比布谷鸟过滤器而言布隆过滤器有以下不足:
查询性能弱、空间利用效率低、不支持反向操作(删除)以及不支持计数
分布式锁
OnlyOne,任何时刻只能有且仅有一个线程持有
若redis集群环境下,不能因为某一个节点挂了而出现获取锁和释放锁失败的情况
杜绝死锁,必须有超时控制机制或者撤销操作,有个兜底终止跳出方案
防止张冠李戴,不能私下unlock别人的锁,只能自己加锁自己释放。
同一个节点的同一个线程如果获得锁之后,它也可以再次获取这个锁。
Redis分布式锁比较正确的姿势是采用redisson这个客户端工具
Redisson是java的redis客户端之一,提供了一些api方便操作redis
publicstaticbooleantryLock(Stringkey,StringuniqueId,intseconds){
return"OK".equals(jedis.set(key,uniqueId,"NX","EX",seconds));
}
解锁关键逻辑:
publicstaticbooleanreleaseLock(Stringkey,StringuniqueId){
StringluaScript="ifredis.call('get',KEYS[1])==ARGV[1]then"+
"returnredis.call('del',KEYS[1])elsereturn0end";
returnjedis.eval(
luaScript,
Collections.singletonList(key),
Collections.singletonList(uniqueId)
).equals(1L);
上面一般中小公司,不是高并发场景,是可以使用的。单机redis小业务也撑得住
线程1首先获取锁成功,将键值对写入redis的master节点;
在redis将该键值对同步到slave节点之前,master发生了故障;
redis触发故障转移,其中一个slave升级为新的master;
此时新的master并不包含线程1写入的键值对,因此线程2尝试获取锁也可以成功拿到锁;
此时相当于有两个线程获取到了锁,可能会导致各种预期之外的情况发生,例如最常见的脏数据。
Redis也提供了Redlock算法,用来实现基于多个实例的分布式锁。
锁变量由多个实例维护,即使有实例发生了故障,锁变量仍然是存在的,客户端还是可以完成锁操作。Redlock算法是实现高可靠分布式锁的一种有效解决方案,可以在实际开发中使用。
redis异步复制造成的锁丢失,
比如:主节点没来的及把刚刚set进来这条数据给从节点,就挂了。
该方案也是基于(set加锁、Lua脚本解锁)进行改良的,所以redis之父antirez只描述了差异的地方,大致方案如下。
假设我们有N个Redis主节点,例如N=5这些节点是完全独立的,我们不使用复制或任何其他隐式协调系统,为了取到锁客户端执行以下操作:
该方案为了解决数据不一致的问题,直接舍弃了异步复制只使用master节点,同时由于舍弃了slave,为了保证可用性,引入了N个节点,官方建议是5。本次用3台实例来做说明。
客户端只有在满足下面的这两个条件时,才能认为是加锁成功。
条件1:客户端从超过半数(大于等于N/2+1)的Redis实例上成功获取到了锁;
为什么是奇数?N=2X+1(N是最终部署机器数,X是容错机器数)
1先知道什么是容错
失败了多少个机器实例后我还是可以容忍的,所谓的容忍就是数据一致性还是可以Ok的,CP数据一致性还是可以满足
加入在集群环境中,redis失败1台,可接受。2X+1=2*1+1=3,部署3台,死了1个剩下2个可以正常工作,那就部署3台。
加入在集群环境中,redis失败2台,可接受。2X+1=2*2+1=5,部署5台,死了2个剩下3个可以正常工作,那就部署5台。
2为什么是奇数?
最少的机器,最多的产出效果
加入在集群环境中,redis失败1台,可接受。2N+2=2*1+2=4,部署4台
加入在集群环境中,redis失败2台,可接受。2N+2=2*2+2=6,部署6台
dockerrun-p6381:6379--nameredis-master-1-dredis:6.0.7
dockerrun-p6382:6379--nameredis-master-2-dredis:6.0.7
dockerrun-p6383:6379--nameredis-master-3-dredis:6.0.7
综上所述,虽然Redlock算法在理论上看起来非常可靠,但是在实际使用中存在一些问题,需要根据具体情况进行评估和选择,或者采用其他更加可靠的分布式锁算法来替代Redlock算法。
分布式难以避免的,系统时钟影响
在获取锁成功后,给锁加一个watchdog,watchdog会起一个定时任务,在锁没有被释放且快要过期的时候会续期
在Redisson中,internalLockLeaseTime是30s,也就是每隔10s续期一次,每次30s。
一般推荐Redis设置内存为最大物理内存的四分之三
Redis的内存设置应该根据具体的应用场景和需求来进行调整。一般来说,需要考虑以下几个方面:
因此,对于生产环境的Redis内存设置,需要根据具体的业务需求和系统规模进行调整,并进行充分的测试和评估,以确保Redis能够稳定运行并满足业务需求。
打开redis配置文件,设置maxmemory参数,maxmemory是bytes字节类型,注意转换。
因为Redis内存的设置需要根据具体的业务需求和系统规模来调整,不同的应用场景和业务负载下,Redis内存的设置可能会有所不同。
一般来说,Redis的内存设置应该考虑以下几个因素:
需要注意的是,Redis内存的设置需要综合考虑多方面的因素,包括系统规模、业务负载、数据类型和访问频率等因素,同时需要进行实时监控和调整,以保证系统的高效和稳定。
NO
如果未过期,返回数据;
发现已过期,删除,返回不存在。
惰性删除策略的缺点是,它对内存是最不友好的
如果一个键已经过期,而这个键又仍然保留在redis中,那么只要这个过期键不被删除,它所占用的内存就不会释放。
在使用惰性删除策略时,如果数据库中有非常多的过期键,而这些过期键又恰好没有被访问到的话,那么它们也许永远也不会被删除(除非用户手动执行FLUSHDB),我们甚至可以将这种情况看作是一种内存泄漏–无用的垃圾数据占用了大量的内存,而服务器却不会自己去释放它们,这对于运行状态非常依赖于内存的Redis服务器来说,肯定不是一个好消息
定期删除策略是前两种策略的折中:
周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度
特点1:CPU性能占用设置有峰值,检测频度可自定义设置
特点2:内存压力不是很大,长期占用内存的冷数据会被持续清理
总结:周期性抽查存储空间(随机抽查,重点抽查)
总结:定期抽样key,判断是否过期会有漏网之鱼
1定期删除时,从来没有被抽查到
2惰性删除时,也从来没有被点中使用过
上述2步骤======>大量过期的key堆积在内存中,导致redis内存空间紧张或者很快耗尽
当缓存对象需要淘汰时,Redis会从双向链表的尾部开始查找最近最少使用的缓存对象,并将其从哈希表和双向链表中同时删除。当有新的缓存对象加入时,Redis会将其插入双向链表的头部,并在哈希表中添加对应的键和指针。
canal[k'nl],中文翻译为水道/管道/沟渠/运河,主要用途是用于MySQL数据库增量日志数据的订阅、消费和解析,是阿里巴巴开发并开源的,采用Java语言开发;
历史背景是早期阿里巴巴因为杭州和美国双机房部署,存在跨机房数据同步的业务需求,实现方式主要是基于业务trigger(触发器)获取增量变更。从2010年开始,阿里巴巴逐步尝试采用解析数据库日志获取增量变更进行同步,由此衍生出了canal项目;
Canal是基于MySQL变更日志增量订阅和消费的组件
MySQL的主从复制将经过如下步骤:
1、当master主服务器上的数据发生改变时,则将其改变写入二进制事件日志文件中;
3、同时master主服务器为每个I/OThread启动一个dumpThread,用于向其发送二进制事件日志;
4、slave从服务器将接收到的二进制事件日志保存至自己本地的中继日志文件中;
5、salve从服务器将启动SQLThread从中继日志中读取二进制日志,在本地重放,使得其数据和主服务器保持一致;
6、最后I/OThread和SQLThread将进入睡眠状态,等待下一次被唤醒;
canal工作原理
canal模拟MySQLslave的交互协议,伪装自己为MySQLslave,向MySQLmaster发送dump协议
MySQLmaster收到dump请求,开始推送binarylog给slave(即canal)
canal解析binarylog对象(原始为byte流)
分布式系统只有最终一致性,很难做到强一致性
D:\devSoft\mysql\mysql5.7.28目录下打开my.ini
最好备份
log-bin=mysql-bin#开启binlog
binlog-format=ROW#选择ROW模式
server_id=1#配置MySQLreplaction需要定义,不要和canal的slaveId重复
ROW模式除了记录sql语句之外,还会记录每个字段的变化情况,能够清楚的记录每行数据的变化历史,但会占用较多的空间。
STATEMENT模式只记录了sql语句,但是没有记录上下文信息,在进行数据恢复的时候可能会导致数据的丢失情况;
MIX模式比较灵活的记录,理论上说当遇到了表结构变更的时候,就会记录为statement模式。当遇到了数据更新或者删除情况下就会变为row模式;
windowmy.ini
linuxmy.cnf
重启mysql
mysql默认的用户在mysql库的user表里
DROPUSER'canal'@'%';
CREATEUSER'canal'@'%'IDENTIFIEDBY'canal';
GRANTALLPRIVILEGESON.TO'canal'@'%'IDENTIFIEDBY'canal';
FLUSHPRIVILEGES;
SELECT*FROMmysql.user;
数据库中的值要是最新值
单线程,这样重量级的数据操作最好不要多线程
总之,我们要达到最终一致性!
1先更新mysql的某商品的库存,当前商品的库存是100,更新为99个。
2先更新mysql修改为99成功,然后更新redis。3此时假设异常出现,更新redis失败了,这导致mysql里面的库存是99而redis里面的还是100。4上述发生,会让数据库里面和缓存redis里面数据不一致,读到脏数据
这里写20秒,是自己故意乱写的,表示更新数据库可能失败,实际中不可能...O(∩_∩)O哈哈~
1A线程先成功删除了redis里面的数据,然后去更新mysql,此时mysql正在更新中,还没有结束。(比如网络延时)
B突然出现要来读取缓存数据。
2此时redis里面的数据是空的,B线程来读取,先去读redis里数据(已经被A线程delete掉了),此处出来2个问题:
2.1B从mysql获得了旧值
B线程发现redis里没有(缓存缺失)马上去mysql里面读取,从数据库里面读取来的是旧值。
2.2B会把获得的旧值写回redis
获得旧值数据后返回前台并回写进redis(刚被A线程删除的旧数据有极大可能又被写回了)。
3A线程更新完mysql,发现redis里面的缓存是脏数据,A线程直接懵逼了,o(╥﹏╥)o
两个并发操作,一个是更新操作,另一个是查询操作,A更新操作删除缓存后,B查询操作没有命中缓存,B先把老数据读出来后放到缓存中,然后A更新操作更新了数据库。
于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的,而且还一直这样脏下去了。
4总结流程:
(1)请求A进行写操作,删除缓存后,工作正在进行中......A还么有彻底更新完
(2)请求B开工,查询redis发现缓存不存在
(3)请求B继续,去数据库查询得到了myslq中的旧值
(4)请求B将旧值写入redis缓存
(5)请求A将新值写入mysql数据库
上述情况就会导致不一致的情形出现。
解决方案
多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它。
其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。
后面的线程进来发现已经有缓存了,就直接走缓存。
采用延时双删策略
双删方案面试题
这个删除该休眠多久呢
这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。
当前演示的效果是mysql单机,如果mysql主从读写分离架构如何?
(1)请求A进行写操作,删除缓存
(2)请求A将数据写入数据库了,
(3)请求B查询缓存发现,缓存没有值
(4)请求B去从库查询,这时,还没有完成主从同步,因此查询到的是旧值
(5)请求B将旧值写入缓存
(6)数据库完成主从同步,从库变为新值上述情形,就是数据不一致的原因。还是使用双删延时策略。
这种同步淘汰策略,吞吐量降低怎么办?
业务指导思想
1可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用Kafka/RabbitMQ等)。
2当程序没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。
3如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作,此时,我们也可以保证数据库和缓存的数据一致了,否则还需要再次进行重试
4如果重试超过的一定次数后还是没有成功,我们就需要向业务层发送报错信息了,通知运维人员。
在大多数业务场景下,我们会把Redis作为只读缓存使用。假如定位是只读缓存来说,
理论上我们既可以先删除缓存值再更新数据库,也可以先更新数据库再删除缓存,但是没有完美方案,两害相衡趋其轻的原则
个人建议是,优先使用先更新数据库,再删除缓存的方案。理由如下:
1先删除缓存值再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力,严重导致打满mysql。
多补充一句:如果使用先更新数据库,再删除缓存的方案
如果业务层要求必须读取一致性的数据,那么我们就需要在更新数据库时,先在Redis缓存客户端暂存并发读请求,等数据库更新完、缓存值删除后,再读取数据,从而保证数据一致性。
在Redis中,可以使用string和hash来存储对象,它们各有优缺点,应根据具体场景进行选择。
当对象比较简单,只包含一个键值对时,可以使用string类型来存储对象。string类型的数据结构非常简单,存储效率高,适合存储简单的字符串、数字等数据类型。例如,可以使用string类型来存储用户的ID、用户名、年龄等信息。
需要注意的是,当使用hash类型存储对象时,需要考虑到哈希表的扩容和缩容问题,因为哈希表的大小是固定的,当存储的键值对数量超过哈希表的负载因子时,Redis会自动扩容哈希表,从而导致一定的性能损耗和内存浪费。因此,在使用hash类型存储对象时,需要合理设置哈希表的初始大小和负载因子,以避免频繁扩容和浪费内存的情况发生。
以上只是一些简单的使用场景,Redis还有更多的用途,例如发布/订阅、持久化、事务等,可以根据项目需求来选择合适的使用方式。
以上是我对美团附近酒店的设计思路,针对不同的场景和需求,可以做出不同的调整和优化。
结合上图可知,网络IO操作就变成多线程化了,其他核心部分仍然是线程安全的,是个不错的折中办法。
Redis6.0将网络数据读写、请求协议解析通过多个IO线程的来处理,对于真正的命令执行来说,仍然使用主线程操作,一举两得.
Redis中的zset是一种有序集合,它的底层实现采用了压缩列表和跳表的结构。
压缩列表是一种紧凑的线性结构,可以节省内存空间。在zset中,如果集合中的元素数量比较少,可以使用压缩列表来存储数据,因为压缩列表的结构比较简单,只需要存储元素的score和value即可。压缩列表的优点是占用内存小,但缺点是查询效率较低,因为需要遍历整个列表才能找到指定的元素。
综合来看,Redis中的zset底层实现采用了压缩列表和跳表的结构,可以在不同的场景下选择合适的数据结构来存储数据,从而达到节省内存空间和提高查询效率的目的。但是,这样的设计也存在一些缺点,例如在插入或删除元素时需要同时更新压缩列表和跳表,因此会增加一定的复杂度和开销。此外,压缩列表和跳表的结构比较复杂,需要占用一定的计算资源来维护和更新,因此可能会影响系统的性能和稳定性。
Redis中的跳表(SkipList)是一种基于链表的数据结构,可以快速地进行查找、插入和删除操作,常用于实现有序集合和索引等功能。跳表的设计思想是通过增加多级索引来加速查找操作,从而达到快速查询的目的。
跳表中包含多级索引,每一级索引都是一个有序的链表,索引节点的值比下一级索引节点的值大。跳表的最底层索引包含了所有的数据节点,因此可以通过最底层的索引进行查找。
在插入或删除元素时,需要在跳表中找到相应的位置,并更新相邻节点的索引。因为每一级索引的节点数是按照一定概率随机生成的,因此跳表的高度是随机的,但平均高度为O(logn)。
IO多路复用是指在单个线程内同时监视多个文件描述符,从而实现对I/O操作的异步处理。其中,select和epoll是两种常用的IO多路复用机制。
select是Unix系统中最古老的IO多路复用机制,它可以同时监视多个文件描述符,一旦某个文件描述符就绪,就会通知应用程序进行处理。select的缺点是它采用轮询的方式来监视文件描述符,当文件描述符的数量较多时,会导致系统开销较大,因此在高并发场景下效率不高。
epoll是Linux系统中较新的IO多路复用机制,它采用事件驱动的方式来监视文件描述符,并且可以支持较大的并发连接数,因此在高并发场景下效率更高。epoll的优点是它支持边缘触发和水平触发两种模式,可以更加精细地控制事件通知,从而提高了系统的性能和稳定性。
总之,select和epoll都是常用的IO多路复用机制,它们能够实现对多个文件描述符的异步处理和高效监视,但是epoll在高并发场景下的效率更高,因此在实际开发中应该根据具体情况选择合适的IO多路复用机制。
Redis一个字符串类型的值最大能存储512MB的数据。这是由于Redis使用了一个名为sds(simpledynamicstring)的数据结构来存储字符串,其最大长度为2^32-1字节,即4GB,但是Redis规定单个字符串对象的最大长度为512MB,这是为了确保Redis的性能和稳定性,在数据存储和传输过程中不会出现过度消耗内存、影响系统性能的情况。
Redis提供了不同级别的持久化方式:
Redis集群并没有使用一致性hash而是引入了哈希槽的概念。Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。但为什么哈希槽的数量是16384(2^14)个呢?
CRC16算法产生的hash值有16bit,该算法可以产生2^16=65536个值。
换句话说值是分布在0~65535之间。那作者在做mod运算的时候,为什么不mod65536,而选择mod16384?
正常的心跳数据包带有节点的完整配置,可以用幂等方式用旧的节点替换旧节点,以便更新旧的配置。
这意味着它们包含原始节点的插槽配置,该节点使用2k的空间和16k的插槽,但是会使用8k的空间(使用65k的插槽)。
同时,由于其他设计折衷,Redis集群不太可能扩展到1000个以上的主节点。
因此16k处于正确的范围内,以确保每个主机具有足够的插槽,最多可容纳1000个矩阵,但数量足够少,可以轻松地将插槽配置作为原始位图传播。请注意,在小型群集中,位图将难以压缩,因为当N较小时,位图将设置的slotN位占设置位的很大百分比。
(1)如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。当槽位为65536时,这块的大小是:65536÷8÷1024=8kb
因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。
(2)redis的集群主节点数量基本不可能超过1000个。
集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者不建议rediscluster节点数量超过1000个。那么,对于节点数在1000以内的rediscluster集群,16384个槽位够用了。没有必要拓展到65536个。
(3)槽位越小,节点少的情况下,压缩比高,容易传输
Redis主节点的配置信息中它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中会对bitmap进行压缩,但是如果bitmap的填充率slotsN很高的话(N表示节点数),bitmap的压缩率就很低。如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。
要保证Redis中存储的数据都是热点数据,可以考虑以下几种方式:
综上所述,保证Redis中存储的数据都是热点数据,需要结合实际情况采取不同的策略,同时需要根据访问频率和数据量等因素来控制Redis中的缓存大小和数据淘汰策略,从而实现高效的数据存储和访问。
请求去查询一条记录,先redis后mysql发现都查询不到该条记录,但是请求每次都会打到数据库上面去,导致后台数据库压力暴增,这种现象我们称为缓存穿透,这个redis变成了一个摆设。。。。。。
key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
简单说就是本来无一物,既不在Redis缓存中,也不在数据库中
黑客会对你的系统进行攻击,拿一个不存在的id去查询数据,会产生大量的请求到数据库去查询。可能会导致你的数据库由于压力过大而宕掉
id相同打你系统:第一次打到mysql,空对象缓存后第二次就返回null了,避免mysql被攻击,不用再到数据库中去走一圈了
Guava中布隆过滤器的实现算是比较权威的,所以实际项目中我们不需要手动实现一个布隆过滤器
原始样本:100W
不存在数据:101W---110W
Guava缺点说明:
Guava提供的布隆过滤器的实现还是很不错的(想要详细了解的可以看一下它的源码实现),但是它有一个重大的缺陷就是只能单机使用,而现在互联网一般都是分布式的场景。
为了解决这个问题,我们就需要用到Redis中的布隆过滤器了
案例:白名单过滤器
简单说就是热点key突然失效了,暴打mysql
危害:会造成某一时刻数据库请求量过大,压力剧增。
高并发的淘宝聚划算案例落地
需要注意的是,大key不仅会影响Redis的性能和稳定性,还会导致Redis集群中的数据分布不均,从而影响集群的负载均衡和可用性。因此,在使用Redis时,应该避免存储过大的数据量到单个key中,尽量采用拆分、分片等方式来分散数据,保证Redis的高效和稳定。
AOF(AppendOnlyFile:将redis所执行过的所有指令都记录下来,在下次redis重启时,只需要执行指令就可以了):写日志。缺点:体积大,恢复速度慢