最简单的字符串类型键值对缓存,也是最基本的,二进制安全
get/set/del:查询/设置/删除
set rekey data:设置已经存在的key,会覆盖
setnx rekey data:设置已经存在的key,不会覆盖,还是原来的值
set key value ex time:设置带过期时间的数据
expire key:设置过期时间
ttl:查看剩余时间,-1永不过期,-2过期
append key:合并字符串
strlen key:字符串长度
incr key:累加1
decr key:类减1
incrby key num:累加给定数值
decrby key num:累减给定数值
getrange key start end:截取数据,end=-1 代表到最后
setrange key start newdata:从start位置开始替换数据
mset:连续设值
mget:连续取值
msetnx:连续设置,如果存在则不设置
select index:切换数据库,总共默认16个
keys *:查看所有的key (不建议在生产上使用,有性能影响)
type key:key的类型
flushdb:删除当前下边db中的数据
flushall:删除所有db中的数据
类似map,存储结构化数据结构,比如存储一个对象(不能有嵌套对象),适合存储一些简单的json格式的数据,这种存储结构可以使数据更加清晰明了
hset key property value:
hset user name zhangsan
创建一个user对象,这个对象中包含name属性,name值为zhangsan
hget user name:获得用户对象中name的值
hmset:设置对象中的多个键值对,会覆盖
hmset user age 18 phone 139123123
创建/设置user对象,这个对象中包含age和phone属性,age值为18,phone值为139123123
hmsetnx:设置对象中的多个键值对,不会覆盖
hmget:获得对象中的多个属性
hmget user age phone
hgetall user:获得整个对象的内容
hincrby user age 2:累加属性
hincrbyfloat user age 2.2:累加属性
hlen user:有多少个属性
hexists user age:判断属性是否存在
hkeys user:获得所有属性
hvals user:获得所有值
hdel user:删除对象
列表,按照String元素插入顺序排序,类似与栈,先进后出,底层数据结构是压缩列表和双向链表两种
例:
lpush mylist aaa
lpush mylist bbb
lpush mylist ccc
lrange mylist 0 -1
(返回“ccc” “bbb” “aaa”)
lpush userList 1 2 3 4 5:构建一个list,从左边开始存入数据
rpush userList 1 2 3 4 5:构建一个list,从右边开始存入数据
lrange list start end:获得数据
lpop:从左侧开始拿出一个数据
rpop:从右侧开始拿出一个数据
llen list:list长度
lindex list index:获取list下标的值
lset list index value:把某个下标的值替换
linsert list before/after value:插入一个新的值
lrem list num value:删除几个相同数据
ltrim list start end:截取值,替换原来的list
无序集合,通过哈希表实现,不允许重复,添加、删除或查询某一元素等操作。需要说明的是,这些操作的时间复杂度为O(1),可以很轻易的达到去重的效果,比如可以将所有关注某个博主的粉丝存在同一个集合中,同时redis提供了方便的求交集、并集、差集等操作,所以可以非常方便的实现共同关注、共同喜好的功能。
sadd set duck pig cow sheep sheep sheep pig:构建一个set,只能存入不重复的4个数据(duck pig cow sheep)
smembers set:查看set中所有的数据
scard set:查看set中有多少个数据
sismembers set pig:判断pig在不在set里,1代表在,0代表不存在
srem set duck:删除set中的duck数据,1代表成功删除1项,0代表失败
spop set count:在set中随机删除count个数据
srandmember set count:从set中随机获取count个数据
smove set1 set2 10:从set1中移动10到set2中,1代表成功移动1项
sdiff set1 set2:列出在set1不在set2的数据,差集
sinter set1 set2:列出在set1且在set2的数据,交集
sunion set1 set2:列出set1和set2并集的数据
排序的set,可以去重可以排序,比如可以根据用户积分做排名,积分作为set的一个数值,根据数值可以做排序。set中的每一个memeber都带有一个分数
zadd zset 10 value1 20 value2 30 value3:设置member和对应的分数
zrange zset 0 -1:查看所有zset中的内容
zrange zset 0 -1 withscores:带有分数
zrank zset value:获得对应的下标
zscore zset value:获得对应的分数
zcard zset:统计个数
zcount zset 分数1 分数2:统计个数
zrangebyscore zset 分数1 分数2:查询分数之间的member(包含分数1 分数2)
zrangebyscore zset (分数1 (分数2:查询分数之间的member(不包含分数1 和 分数2)
zrangebyscore zset 分数1 分数2 limit start end:查询分数之间的member(包含分数1 分数2),获得的结果集再次根据下标区间做查询
zrem zset value:删除member
用于计数的HyperLogLog,用于支持存储地理位置信息的Geo等
用于布隆过滤器,用于判断数据是否存在(解决缓存穿透的方案之一)
阻塞是值指服务器必须要把第一个客户的请求处理完成,才能处理第二个客户的请求。非阻塞是指服务器可以不能第一个用户响应完成请求即可处理第二甚至多个用户的请求。
多路复用器是值一个用户请求后的后续处理操作不由多路复用器处理,交由后续处理,该多路复用器可以接着处理下一个用户的请求。
建立连接时,Redis-Cli会发送一个Read事件到Redis-Server,然后Redis-Server里就会出现一个Server Socket(即socket连接),再交由多路复用器,多路复用器拿到后就会丢到队列里,队列都会到达文件事件分配器,文件事件分配器检测到是一个Read事件后,就会交由连接应答处理器,这时就代表Redis-Cli和Redis-Server之间建立了连接,之后Read标识的事件就会交由命令请求处理器。后续假设Redis-Cli发送一个set的请求,就会走同样的路线,走到命令请求处理器,处理请求,Write标识的事件代表是回写,如OK,会通过同样的路线走到命令回复处理器。多个客户端同理。redis多路复用器后续操作是基于内存处理的,非常快速。
同一个redis服务器启动三个客户端连接,其中一个客户端模拟发布者,其他两个客户端模拟订阅者。
PUBLISH food duck:表示推送food这个”频道“,duck这个数据
SUBSCRIBE food drink:表示订阅food和drink两个”频道“
PSUBSCRIBE China*:批量订阅,表示China这个字符开头的”频道“都订阅
只能起到基本的发布订阅作用,不能保证消息的容错性和可靠性,如果项目中发布的消息比较重要,不建议使用redis做消息处理,推荐使用MQ。
https://redis.io/docs/manual/persistence/
如果没有使用redis持久化机制,redis中的数据库就会与服务器的存活时间一样,服务器宕机redis数据就会消失。
但是实际开发中,很少使用redis持久化,redis只是作缓存使用,如果想要持久化还是会使用关系型数据库。
RDB: Redis DataBase
AOF: Append Only File
RDB:每隔一段时间,把内存中的数据写入磁盘的临时文件,作为快照,恢复的时候把快照文件读进内存。如果宕机重启,那么内存里的数据肯定会没有的,那么再次启动redis后,则会恢复。
内存备份 --> 磁盘临时文件
临时文件 --> 恢复到内存
save 900 1 # 如果1个缓存更新,则15分钟后备份
save 300 10 # 如果10个缓存更新,则5分钟后备份
save 60 10000 # 如果10000个缓存更新,则1分钟后备份
save 10 3 # 演示:更新3个缓存,10秒后备份
stop-writes-on-bgsave-error yes # yes:如果save过程出错,则停止写操作;no:可能造成数据不一致
rdbcompression yes # yes:开启rdb压缩模式;no:关闭,会节约cpu损耗,但是文件会大,道理同nginx
rdbchecksum yes # yes:使用CRC64算法校验对rdb进行数据校验,有10%性能损耗;no:不校验
dbfilename dump.rdb # rdb文件名称
RDB适合大量数据的恢复,但是数据的完整性和一致性可能会不足
RDB会丢失最后一次备份的rdb文件,但是其实也无所谓,其实也可以忽略不计,毕竟是缓存,丢了就丢了,但是如果追求数据的完整性,那就的考虑使用AOF了。
# AOF 默认关闭,yes可以开启
appendonly no
# AOF 的文件名
appendfilename "appendonly.aof"
# no:不同步
# everysec:每秒备份,推荐使用
# always:每次操作都会备份,安全并且数据完整,但是慢性能差
appendfsync everysec
# 重写的时候是否要同步,no可以保证数据安全
no-appendfsync-on-rewrite no
# 重写机制:避免文件越来越大,自动优化压缩指令,会fork一个新的进程去完成重写动作,新进程里的内存数据会被重写,此时旧的aof文件不会被读取使用,类似rdb
# 当前AOF文件的大小是上次AOF大小的100% 并且文件体积达到64m,满足两者则触发重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
单实例的redis可以为数据库做到一个屏障的作用,一般来说单机单个节点,redis的并发在5-6万左右,存在瓶颈,可以使用redis作一个主从架构(也叫做读写分离结构),一般情况,用户大部分请求都是读请求,可以交给从redis做,少部分写的请求可以交给主redis做。
从节点slave配置并且启动后,会发送一个ping包给master节点,ping通后,master节点会将一个RDB文件从内存里拿出来,放入磁盘,通过内网传输传递给slave节点,slave节点在拿到RDB文件文件首先进行下载到磁盘,再加载到内存中,这第一次相当于init操作,后续只要有写操作, 都会将相应的数据传输给slave节点,这样slave节点就会修改自己内存中的数据,这样master节点和slave节点之间就可以进行数据同步,slave节点就能为外部提供读的请求了。且master节点的写操作和slave节点的读操作互不影响,互不阻塞,因为这两个节点在数据没有同步完成,都会使用老的数据进行操作,初次是全量同步,后续同步为增量。注意:使用主从架构必须要开启master节点的持久化,如果不开启,一旦master节点宕机,虽然slave节点仍然可以提供读的服务,但是一旦master节点重新恢复运作,此时其内存里是没有任何数据的,这样再次作数据同步时会将slave节点数据清空。
# 配置主节点的ip和端口号
# slaveof
slaveof 192.168.0.131 6379
# 主节点master的密码
# masterauth
masterauth 123456
# 所有的从节点默认yes只有读的权限,没有写的权限
slave-read-only yes在配置主从模式时,通常都是一主二从的模式。
一般使用一主二从,从节点过多可能会导致同步时一直在占用内网网络带宽。如果一主二从的模式还是不能满足我们并发的响应需求,可以采取下面这种,从节点再套从节点的模式,实现第二个主从,即树状结构。
上面介绍的主从复制模式是redis默认的磁盘复制,redis还有一种无磁盘化复制,通过内存传输的,是通过socket方式,基于网络的,这种方式主要是一因为服务器的磁盘类型,云服务器的硬盘类型如果是最普通的机械硬盘,那么它的磁盘读写(即IO)是相当底下的,此时这种无磁盘化复制就避免了磁盘读写,从而提高效率。如果此时内网带宽高,就可以采用无磁盘化复制方式替换默认的磁盘复制方式,这种方式默认是关闭的,可以在redis的配置文件中开启。
# 默认关闭无磁盘化复制方式
repl-diskless-sync no
计算机内存有限,越大越贵,Redis的高并发高性能都是基于内存的,用硬盘的话GG。
redis的高并发、高性能都是基于内存的,Redis 缓存过期机制是针对设置过expire过期时间的key,然后expire时间已经过了,这些key虽然查询不了,但是还是会占用服务器的内存,故存在以下两种策略。
redis默认每秒10次的随机检测过期的key,根据自己系统的情况,可用设置1-500次,但是设置的越高,占用的cpu资源越高,通常不建议超过100次
# By default "hz" is set to 10. Raising the value will use more CPU when
# Redis is idle, but at the same time will make Redis more responsive when
# there are many keys expiring at the same time, and timeouts may be
# handled with more precision.
#
# The range is between 1 and 500, however a value over 100 is usually not
# a good idea. Most users should use the default of 10 and raise this up to
# 100 only in environments where very low latency is required.
hz 10
计算机的内存是有限的,当一台计算机上部署了除redis的如tomcat、kafka等其他中间件,也会占用内存,故redis提供可以设置其占用内存的阈值,设置阈值后,redis会主动清理那些存在内存永久存在没有过期时间的key,可以在配置文件配置。
前面说到了redis的主从模式,但是如果Master挂了,slave节点就只能进行读请求,那如何保证可用性,继续实现读写操作呢?
Sentinel(哨兵)是用于监控Redis集群中Master状态的工具,是 Redis 高可用解决方案,哨兵可以监视一个或者多个redis master服务,以及这些master服务的所有从服务;当某个master服务宕机后,会把这个master下的某个从服务升级为master来替代已宕机的master继续工作。
创建并且配置sentinel.conf:
普通配置
# 端口号
port 26379
# 进程的pid,和redis主进程不是同一个pid
pidfile "/usr/local/redis/sentinel/redis-sentinel.pid"
# 工作空间
dir "/usr/local/redis/sentinel"
# 配置后台运行哨兵
daemonize yes
protected-mode no
# 日志文件的位置
logfile "/usr/local/redis/sentinel/redis-sentinel.log"
核心配置
# 配置哨兵的名称 和master的内网ip和端口号 2代表多少个哨兵在同一时间发现master连接不上就进行故障转移机制
# 假设5个哨兵节点,有2个在同一发现master连接不上,2个节点的其中一个节点就可以开启故障转移了
sentinel monitor mymaster 127.0.0.1 6379 2
# 密码
sentinel auth-pass <master-name> <password>
# master被sentinel认定为失效的间隔时间,默认为30S
sentinel down-after-milliseconds mymaster 30000
# 剩余的slaves重新和新的master做同步的并行个数
sentinel parallel-syncs mymaster 1
# 主备切换的超时时间,哨兵要去做故障转移,这个时候哨兵也是一个进程,如果他没有去执行,超过这个时间后,会由其他的哨兵来处理
sentinel failover-timeout mymaster 180000
redis-sentinel sentinel.conf
问题:原来的Master(191)恢复成Slave后,他的同步状态不OK,状态为master_link_status:down
,这是为什么呢?
这是因为我们只设置了192和193的masterauth
,这是用于同步master的数据,但是191一开始是master是不受影响的,当master转变为slave后,由于他没有设置masterauth
,所以他不能从新的master同步数据,随之导致info replication
的时候,同步状态为down
,所以只需要修改redis.conf
中的masterauth
为redis的密码即可。
一般master数据无法同步给slave的方案检查为如下:
master挂了以后,由于哨兵监控,剩余slave会进行选举,选举后其中一个成为master,当原来的master恢复后,他会成为slave。
哨兵节点一般为集群模式,当只有一个哨兵节点觉得我们的redis master节点宕机,其他哨兵节点仍然可以ping通master节点,这种情况称为主观下线,有可能是由于网络的原因,可以设置quorum=2这样有两个哨兵节点感知master节点宕机,这种情况称为客观下线,这个时候就可以断定master节点宕机。之后就可以做故障转移。由于redis哨兵节点如果是双数有可能出现对半分的情况,所以尽量使得redis的节点是单数,提高客观下线准确度。
哨兵之间会通过选举获取一个老大,老大就会负责故障转移的工作,即将slave转变为master,当故障转移成功后就会进行数据的同步,sentinel parallel-syncs master num
可以通过这个配置进行数据的同步,num表示一次同步几台节点。
原来的master恢复后就会成为slave
# 查看imooc-master下的master节点信息
sentinel master imooc-master
# 查看imooc-master下的slaves节点信息
sentinel slaves imooc-master
# 查看imooc-master下的哨兵节点信息
sentinel sentinels imooc-master
spring:
redis:
database: 1
password: 123456
sentinel:
master: imooc-master
nodes: 192.168.1.191:26379,192.168.1.192:26379,192.168.1.193:26379
通过实现redis的主从结构和哨兵模式可以提高读请求的并发,但是单个master节点的容量是有限的,如果数据达到一定程度,会出现瓶颈,此时可以通过水平扩展的形式可以将其扩展为多个master节点和slave节点,成为多主多从,即集群,这样就可以实现海量数据的存储,实现高并发和高可用。主从结构本身也是一种集群,可以提高读请求的并发,但是容错方面会有问题,比如master节点在同步数据给slave节点,属于异步复制,假设在复制的过程中,master节点宕机,slave节点上面的数据肯定没有master节点上面节点数据新,当数据同步时,肯定存在时间延迟,在这一段时间的数据就会被丢失,即老master节点变为新的salve节点后,因为宕机未复制的数据就会丢失。这个就是主从结构存在的问题。且哨兵选举和故障转移也要耗费零点几秒甚至几秒的时间,在这段时间内,redis是无法提供写操作的,故而无法保证redis的高可用性,为保证缓存的高可用,我们可用采用redis的集群模式(通常是三主三从,也就是6个节点)。
Redis的三主三从集群模式,每一个master节点下面都会挂上一个slave节点,如果其中某一个master节点宕机,slave节点就会成为一个新的master节点,即主从切换,而且每一个节点都知道彼之间的关系。知道自己的角色(master or slave) 且它们彼此之间可以通信和交互,这些节点的关系可以保存到某一个配置文件中,每个节点都会有该配置文件。如果客户端想要和该集群建立连接,只需要和其中某一个节点建立关系即可,如果其中某一个节点宕机,会有超过半数的节点检测到其宕机,就会发起主从切换,跟之前的哨兵模式一样。三主三从的结构是非常经典的redis架构,对比只有三个主节点形成的集群容错更好。
# 开启集群模式
cluster-enabled yes
# 每一个节点需要有一个配置文件,需要6份。每个节点处于集群的角色都需要告知其他所有节点,彼此知道,这个文件用于存储集群模式下的集群状态等信息,这个文件是由redis自己维护,我们不用管。如果你要重新创建集群,那么把这个文件删了就行
cluster-config-file nodes-201.conf
# 超时时间,超时则认为master宕机,随后主备切换
cluster-node-timeout 5000
# 开启AOF
appendonly yes
#####
# 注意1:如果你使用的是redis3.x版本,需要使用redis-trib.rb来构建集群,最新版使用C语言来构建了,这个要注意
# 注意2:以下为新版的redis构建方式
#####
# 创建集群,主节点和从节点比例为1,1-3为主,4-6为从,1和4,2和5,3和6分别对应为主从关系,这也是最经典用的最多的集群模式
redis-cli -a password --cluster create ip1:port1 ip2:port2 ip3:port3 ip4:port4 ip5:port5 ip6:port6 --cluster-replicas 1
redis-cli -a password --cluster check 192.168.25.64:6380
redis-cli -a password --cluster fix 192.168.218.10:6379
只有master节点有slot槽节点,slave节点没有
进入集群控制台命令:redis-cli -c -a 123456 -h 192.168.218.10 -p 6379
检查集群信息:cluster info
/ cluster nodes
get key命令可以获取集群中任何key的信息,但是keys *命令只能获取本master节点的数据,因为槽slot的原因。槽slot才是真正存储数据的地方,get key命令可以通过Redirected to slot [5798] located at 192.168.218.11:6379获取
spring:
redis:
password: 123456
cluster:
nodes: 192.168.218.10:6379,192.168.218.11:6379,192.168.218.12:6379,192.168.218.13:6379,192.168.218.14:6379,192.168.218.15:6379
在Redis 集群模式Cluster中,Redis采用的是分片Sharding的方式,也就是将数据采用一定的分区策略,分发到相应的集群节点中。
但是我们使用上述HASH算法进行缓存时,会出现一些缺陷,主要体现在服务器数量变动(根据业务情况动态扩容,或节点宕机等场景)的时候,所有缓存的位置都要发生改变。
具体来讲就是说:当缓存服务器数量发生变化时,会引起缓存的雪崩,可能会引起整体系统压力过大而崩溃(大量缓存同一时间失效)。
一致性Hash算法使用取模的方法,一致性Hash算法是对2^32取模,什么意思呢?
简单来说:一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-2^32 - 1(即哈希值是一个32位无符号整形),整个哈希环如下:
将服务器的IP或主机名使用hash算法进行计算,确定其在hash环上的位置。
举例:
假设我们有4台缓存服务器,服务器A、服务器B、服务器C,服务器D,那么,在生产环境中,这4台服务器肯定有自己的IP地址或主机名,我们使用它们各自的IP地址或主机名作为关键字进行哈希计算,使用哈希后的结果对2^32取模,可以使用如下公式示意:
hash(服务器A的IP地址) % 2^32
上述公式算出的结果一定是一个0到2^32-1之间的一个整数,上图中的hash环上必定有一个点与这个整数对应,而我们刚才已经说明,使用这个整数代表服务器A,那么,服务器A就可以映射到这个环上。
如下图,节点ABCD通过一致性的hash算法映射到hash的对应位置:
同样的,将数据的key通过同样的hash运算,定位到其在hash环上的位置,然后从此位置沿环顺时针“行走”,遇到的第一台服务器就是其应该定位到的服务器:
现假设Node C不幸宕机,可以看到此时对象A、B、D不会受到影响,只有C对象被重定位到Node D。一般的,在一致性Hash算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响,如下所示:
如果在系统中增加一台服务器Node X,如下图所示:此时对象Object A、B、D不受影响,只有C需要重定位到新的Node X
一般的,在一致性Hash算法中,如果增加一台服务器,则受影响的数据仅仅是新服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它数据不会受到影响。
总结:一致性Hash算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。
一致性Hash算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题,例如系统中服务器和key分布不均时,如下图所示:
此时必然造成大量数据集中到服务器1上,而只有极少量会定位到其他服务器上,从而出现hash环偏斜的情况,当hash环偏斜以后,缓存往往会极度不均衡的分布在各服务器上。
如果想要均衡的将缓存分布到服务器上,最好能让这些服务器尽量多的、均匀的分布在hash环上,但是,真实的服务器资源只有4台,我们怎样让它们多起来呢?既然没有多余的物理服务器节点,我们就只能将现有的物理节点通过虚拟的方法复制出来。
这些由实际节点虚拟复制而来的节点被称为**”虚拟节点“,即对每一个服务节点计算多个哈希**,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器IP或主机名的后面增加编号来实现。
例如上面的情况,假设有只有AB两台服务器,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的哈希值,于是形成六个虚拟节点:
总结:对每一个服务节点计算多个哈希,这样就解决了服务节点少时数据倾斜的问题。在实际应用中,通常将虚拟节点数设置为32甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布 。
缓存击穿:是指一个热点key,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
比如在电商项目中,这个商品就称为“爆款”。
缓存穿透:查询的key在redis中不存在,对应的id在数据库也不存在,此时被非法用户进行攻击,大量的请求会直接打在db上,造成宕机,从而影响整个系统,这种现象称之为缓存穿透。
把空的数据也缓存起来,比如空字符串,空对象,空数组或list。
问题:
布隆过滤器是一种数据结构 ,底层维护了一个只包含0,1数据的数组。
布隆过滤器能够迅速判断一个元素是否在一个集合内,本质是二进制存储,非常小,哪怕是上亿的数据也不占内存,而且查询和添加非常快。比如一个数组,是由二进制存储,0代表不存在,1代表存在,如下,称为布隆过滤器。数据经过hash运算(一种随机映射函数),映射到数组的多个下标位置,如果所有对应的下标位置值都为1,则该数据很可能有;如果对应的下标数字有一个不为1,则一定没有该数据。
注:布隆过滤器能确定一定没有该数据,但不能判断一定有该数据(可以设置布隆过滤器的参数,维护的数组越大,判断越精确,但消耗的空间也越大)
1、会有1%的误判率
2、假设存入布隆过滤器中的key已经不存在db和redis中了,但是无法在布隆过滤器中移除
3、代码的复杂度会增大,需要使用集合,集合中会包含很多key
布隆过滤器可以做邮件、短信的黑名单拦截,或者是缓存穿透等。谷歌对于不良网站的拦截也是使用了布隆过滤器
布隆过滤器可能具有多个hash函数,db和redis一般保存数据对象,而布隆过滤器则会保存该对象对应的唯一键值,比如主键id,拿出该主键id再经过hash函数再放入布隆过滤器,计算看下图,由于误判率的存在可能subCat:9999的数据会被误判为存在,故布隆过滤器可以判断某个数据一定不存在,但是无法判断一个数据一定存在。假设要删除subCat:1,这样subCat:1和subCat:2重复的格子就会被改成0,这样再次查询subCat:2时无法查询到,所以无法移除布隆过滤器中的数据
引入guava依赖
<dependency>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
<version>28.1-jreversion>
dependency>
代码:创建布隆过滤器,向过滤器中放入10000个元素,然后统计错误个数和错误率
public class BloomFilterTest extends SpringBootApplicationTest {
@Test
public void myTest(){
/**
* BloomFilter一共四个create方法,不过最终都是走向第四个。看一下每个参数的含义:
* funnel:数据类型(一般是调用Funnels工具类中的)
* expectedInsertions:期望插入的值的个数
* fpp 错误率(默认值为0.03)
* strategy 哈希算法(指定某个算法)
*/
BloomFilter<CharSequence> bloomFilter = BloomFilter.create(
Funnels.stringFunnel(Charset.forName("utf-8")), 10000
);
for (int i = 0; i < 10000; i++) {
//向布隆过滤器中放入数据
bloomFilter.put(String.valueOf(i));
}
//初始化误判数
int missCount = 0;
for (int i = 0; i < 10000; i++) {
//判断这个数据在布隆过滤器中是否存在
boolean exist = bloomFilter.mightContain("误判" + i);
//如果误判了
if (exist){
missCount ++;
}
}
System.out.println("误判个数为:" + missCount );
System.out.println("误判率为:" + BigDecimal.valueOf(missCount).divide(new BigDecimal(10000),2,ROUND_HALF_UP));
}
}
输出
误判个数为:318
误判率为:0.03
当存在某一个时间点,redis大量key过期,但是此时又恰好有很大的并发量融入进来,那么此时所有的请求都会请求数据库上,此时数据库就会宕机崩溃。
雪崩这种现象是解决不掉的,只能缓解,做防护措施。
1.什么是 Redis?
2.Redis 的数据类型?
3.使用 Redis 有哪些好处?
4.Redis 相比 Memcached 有哪些优势?
5.Memcached 与 Redis 的区别都有哪些?
6.Redis 是单进程单线程的吗?为何它那么快那么高效?
7.一个字符串类型的值能存储最大容量是多少?
8.Redis 的持久化机制是什么?各自的优缺点?
9.Redis 常见性能问题和解决方案有哪些?
10.Redis 过期键的删除策略?
11.Redis 的回收策略(淘汰策略)?
12.为什么Redis 需要把所有数据放到内存中?
13.Redis 的同步机制了解么?
14.Pipeline 有什么好处,为什么要用 Pipeline?
15.是否使用过 Redis 集群,集群的原理是什么?
16.Redis 集群方案什么情况下会导致整个集群不可用?
17.Redis 支持的 Java 客户端都有哪些?官方推荐用哪个?
18.Jedis 与 Redisson 对比有什么优缺点?
19.Redis 如何设置密码及验证密码?
20.说说 Redis 哈希槽的概念?
21.Redis 集群的主从复制模型是怎样的?
22.Redis 集群会有写操作丢失吗?为什么?
23.Redis 集群之间是如何复制的?
24.Redis 集群最大节点个数是多少?
25.Redis 集群如何选择数据库?
26.怎么测试 Redis 的连通性?
27.怎么理解 Redis 事务?
28.Redis 事务相关的命令有哪几个?
29.Redis key 的过期时间和永久有效分别怎么设置?
30.Redis 如何做内存优化?
31.Redis 回收进程如何工作的?
32.都有哪些办法可以降低 Redis 的内存使用情况呢?
33.Redis 的内存用完了会发生什么?
34.一个 Redis 实例最多能存放多少的 keys?List、Set、Sorted Set他们最多能存放多少元素?
35.MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证Redis 中的数据都是热点数据?
36.Redis 最适合的场景是什么?
37.假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?
38.如果有大量的 key 需要设置同一时间过期,一般需要注意什么?
39.使用过 Redis 做异步队列么,你是怎么用的?
40.使用过 Redis 分布式锁么,它是什么回事?
41.如何预防缓存穿透与雪崩?