java面试题核心篇(二)

缓存使用

18、Redis 有哪些类型
string类型:
一个key对应一个value
set mykey "wangzai"             ##设置key,第二次赋值会直接覆盖之前的
setnx mykey "wangzai"           ## 如果mykey存在,则不改变,如果不存在,则创建赋值   
get mykey                       ##获取key的值
setex key1 10 1                 ##给key1设置过期时间为10s,值为1
mset key1 value1 key2 value2    ##设置多个key
mget key1 key2                  ##获取多个key的值
list类型
list是一个链表结构,主要功能是push、pop以及获取一个范围的所有值等。使用list结构,可以轻松实现最新消息排行,另一个应用是消息队列,可以利用list的push操作,将任务存在list中,然后工作线程再用pop操作将任务取出进行执行。(先进后出)
lpush list1 "wangzai"                   ##在列表中加入一个元素    
lrange list1 0 -1                       ##查看list1里面的所有元素
lpop list1                              ##取出list1最新的元素
linsert list1 before "wangzai" "doubi"  ##在值为"wangzai"的前面插入一个元素为"doubi"
lset list1 3 "hehe"                     ##把第五个元素修改为"hehe"
lindex list1 0                          ##查看第一个元素
llen list1                              ##查看列表中有多少元素


set类型
set是集合,对集合操作有添加删除元素,有对多个集合求交并差等操作。在微博应用中,可以将一个用户关注的所有人放在一个集合里,将所有粉丝放在一个集合里,因为redis为集合提供了求交集、并集、差集等操作,就可以方便的实现如共同关注、共同喜好等功能。

sadd set1 a b c d                      ## 创建集合set1并设置值
smembers set1                          ## 查看集合set1的值
srem set1 a b                          ## 删除set1的值
spop set1                              ## 随机取出一个元素并删除
sinter set1 set2                       ## 交集
sinterstore set1 set2 set3             ## 将交集存储到set3 
sunion set1 set2                       ## 并集
sunionstore set1 set2 set3             ## 把并集存储到set3  
sdiff set1 set2                        ## 差集
sdiffstore set1 set2 set3              ## 把差集存储到set3
sismember set1 c                       ## 判断一个元素是否属于一个集合
srandmember set1                       ## 随机取出一个元素,但不删除
sorted set类型
sorted set是有序集合,比set多了一个权重参数score,使得集合元素能够按score进行有序排列。例如存储一个班级同学的成绩,其集合value可以是同学的学号,而score可以是其考试的得分,这样在数据插入集合的时候就进行了排序。
zadd zset1 1 a                  ## 增加一个集合zset1,score为1,member为a
zrange zset1 0 -1               ## 按score升序输出member
zrange zset1 0 -1 withscores    ## 带上分值
zrem zset1 a                    ## 删除指定元素
zrank zset1 a                   ## 返回元素的索引值,索引从0开始
zrevrange zset1 0 -1            ## score降序输出member
zcard zset1                     ## 返回集合中所有元素的个数
zcount zset1 1 10               ## 返回分值范围1-10的元素个数
zrangebyscore zset1 1 10        ## 返回分值范围1-10的元素
zremrangebyscore zset1 1 10     ## 删除分值范围1-10的元素
hash类型
把一些结构化的信息打包成hashmap,在客户端序列化后存储为一个字符串的值(一般为json格式),比如用户姓名、年龄、性别等
hset hash1 name wangzai                  ## 建立hash (hset name key value)
hget hash1 name                          ## 获取field值  HGET name key
hgetall hash1                            ## 获取hash1中所有的key和value
hmset hash2 name wangzai age 26 job it   ## 批量建立键值对
hmget hash2 name age job                 ## 批量获取field值
hdel hash2 job                           ## 删除指定field
hkeys hash2                              ## 打印所有的key
hvals hash2                              ## 打印所有的value
hlen hash2                               ## 查看hash2有几个field
19、Redis 内部结构

(参考https://www.cnblogs.com/chenpingzhao/archive/2017/06/10/6965164.html)

第一个层面,是从使用者的角度,string,list,hash,set,sorted set第二个层面,是从内部实现的角度,属于更底层的实现,   ht(dict),raw,embstr,intset,sds,ziplist,quicklist,skiplist


20、Redis 使用场景
数据缓存
对于热点数据,缓存以后可能读取数十万次,因此,对于热点数据,缓存的价值非常大。例如,分类栏目更新频率不高,但是绝大多数的页面都需要访问这个数据,因此读取频率相当高,可以考虑基于 Redis 实现缓存。
会话缓存
使用 Redis 进行会话缓存。例如,将 web session 存放在 Redis 中。
数据时效性
例如验证码只有60秒有效期,超过时间无法使用,或者基于 Oauth2 的 Token 只能在 5 分钟内使用一次,超过时间也无法使用。
交集、并集和差集
在某些场景中,例如社交场景,通过交集、并集和差集运算,可以非常方便地实现共同好友,共同关注,共同偏好等社交关系。
排行榜/计数器
Redis在内存中对数字进行递增或递减的操作实现的非常好。
最新动态
按照时间顺序排列的最新动态,也是一个很好的应用,可以使用 Sorted Set 类型的分数权重存储 Unix 时间戳进行排序。

21、Redis 持久化机制
22、Redis 如何实现持久化
23、Redis 集群方案与实现
24、Redis 为什么是单线程的
单纯的网络IO来说,量大到一定程度之后,多线程的确有优势, 但并不是单纯的多线程,而是每个线程自己有自己的epoll这样的模型,也就是多线程和multiplexing混合。但是。还要考虑Redis操作的是内存中的数据结构。如果在多线程中操作,那就需要为这些对象加锁。所以使用多线程可以提高性能,但是每个线程的效率严重下降了,而且程序的逻辑严重复杂化。Redis的数据结构并不全是简单的Key-Value,还有list,hash等复杂的结构,这些结构有可能会进行很细粒度的操作,比如在很长的列表后面添加一个元素,在hash当中添加或者删除一个对象,这些操作还可以合成MULTI/EXEC的组。这样一个操作中可能就需要加非常多的锁,导致的结果是同步开销大大增加。Redis在权衡之后的选择是用单线程,突出自己功能的灵活性。在单线程基础上任何原子操作都可以几乎无代价地实现,多么复杂的数据结构都可以轻松运用,甚至可以使用Lua脚本这样的功能。对于多线程来说这需要高得多的代价。


25、缓存中常见的问题及解决方案
缓存一致性
当数据时效性要求很高时,需要保证缓存中的数据与数据库中的保持一致,而且需要保证缓存节点和副本中的数据也保持同步(集群同步)。解决缓存的一致性比较依赖于缓存的过期和更新策略。
缓存并发
缓存过期或者正在更新,同时有大量的并发请求该key。缓存过期,大量请求落到数据库,可能导致“雪崩”发生;如果缓存更新,对某个key有大量的并发请求,此时请求获得的可能是更新之前或者更新之后的,从而会导致“缓存一致性”问题的发生。
解决策略:缓存并发一般发生在查询期间,而且问题出在缓存更新时的高并发时刻,那么就可以在这个时候对key加锁(缓存查询加锁,如果key不存在,然后查出数据写入缓存,然后解锁)。
缓存雪崩
在高并发情况下,缓存更新或者过期导致缓存命中失效,大量请求直接落到数据库,致使其无法承受压力,进而崩溃(缓存并发的递进和升级)。
解决策略:设定一个缓存的过期时间时,可能有一些会设置1分钟后或5分钟后。当并发很高时,会出在某一个时间同时生成了很多的缓存,并且过期时间都一样,这个时候就可能引发一当过期时间到后,这些缓存同时失效,请求全部转发到 DB ,DB可能会压力过重,这样子,我们就可以将缓存过期时间均匀地分布在时间轴上,避免缓存同时失效、更新的情况发生。还需要控制缓存并发、击穿现象的发生,查找大key,想解决方案。
缓存击穿
该key被高并发访问,缓存访问没有命中,尝试去从后端数据库中获取,从而导致了大量请求达到数据库,而当该key对应的数据本身就是空的情况下,这就导致数据库中并发的去执行了很多不必要的查询操作,从而导致巨大冲击和压力。
解决策略:(1)通过设置过滤器(使用布隆过滤器),将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。(2)如果一个查询返回的数据为空(可能由于数据不存在,也可能是系统故障),我们仍然把这个空结果进行缓存,缓存的值设定为一个指定值,同时设置它的过期时间很短,最长不超过五分钟。(因为缓存会占用内存,长时间缓存一个不存在的值比较耗资源。)在这五分钟内,这个值可能由于写入操作从而不再是一个不存在的值,这是就要更新缓存,用真实值替代指定值。


总结:
“并发”和“雪崩”都是高并发时候缓存不被命中直接访问DB的例子,前者是因,或者是果,“并发”造成的影响还不够恶劣,雪崩会导致数据库直接崩溃。“并发”和“击穿”可能会导致“雪崩”发生,避免发生“雪崩”,除了给出的解决方案,更要防止“并发”和“击穿”的发生。


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


27、使用缓存的合理性问题
(1)热点数据,缓存才有价值。
(2)频繁修改的数据,看情况考虑使用缓存。
(3)缓存的不一致性,一般会对缓存设置失效时间,一旦超过失效时间,就要从数据库重新加载,因此应用要容忍一定时间的数据不一致。还有一种是在数据更新时立即更新缓存,不过这也会更多系统开销和事务一致性问题。
(4)缓存更新机制,一般情况下,我们采取缓存双淘汰机制,在更新数据库的时候淘汰缓存。此外,设定超时时间,例如30分钟。极限场景下,即使有脏数据入cache,这个脏数据也最多存在三十分钟。
(5)缓存可用性,缓存是提高数据读取性能的,缓存数据丢失和缓存不可用不会影响应用程序的处理。因此,一般的操作手段是,如果Redis出现异常,我们手动捕获这个异常,记录日志,并且去数据库查询数据返回给用户。
(6)缓存服务降级,服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
(7)缓存预热,在新启动的缓存系统中,如果没有任何数据,在重建缓存数据过程中,系统的性能和数据库复制都不太好,那么最好的缓存系统启动时就把热点数据加载好,例如对于缓存信息,在启动缓存加载数据库中全部数据进行预热。一般情况下,我们会开通一个同步数据的接口,进行缓存预热。

(8)缓存穿透,如果因为不恰当的业务,或者恶意攻击持续地发请求某些不存在的数据,由于缓存没有保存该数据,所有的请求都会落到数据库上,会对数据库造成很大压力,甚至奔溃。一个简单的对策是将不存在的数据也缓存起来。

消息队列

28、消息队列的使用场景
(1)数据驱动的任务依赖,task3需要使用task2的输出作为输入,task2需要使用task1的输出作为输入。MQ只用来传递上游任务执行完成的消息,并不用于传递真正的输入输出数据。
(2)上游不关心执行结果,帖子发布成功后,向MQ发一个消息,哪个下游关注“帖子发布成功”的消息,主动去MQ订阅。
(3)上游关注执行结果,但执行时间很长。
什么时候不使用MQ?
上游实时关注执行结果
什么时候使用MQ?
数据驱动的任务依赖,上游不关心多下游执行结果,异步返回执行时间长。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值