九层之台始于垒土

千里之堤溃于蚁穴

面试简单整理之Redis

179.redis 是什么?都有哪些使用场景?

Redis是一个key-value存储系统。

缓存,消息队列,排行榜/计数器,分布式架构,做session共享
View Code

180.redis 有哪些功能?

181.redis 和 memecache 有什么区别?

1 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache支持简单的数据类型,String。

2 、Redis支持数据的备份,即master-slave模式的数据备份。

3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memecache把数据全部存在内存之中

4、 redis的速度比memcached快很多

5、Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的IO复用模型。
View Code

182.redis 为什么是单线程的?

数据都存在内存里,单线程避免多线程多上下文的切换浪费时间
View Code

 

183.什么是缓存穿透?怎么解决?

缓存穿透,是指查询一个数据库一定不存在的数据。
解决:会采用缓存空值的方式,如果从数据库查询的对象为空,也放入缓存,只是设定的缓存过期时间较短,比如设置为60秒。

缓存雪崩,是指在某一个时间段,缓存集中过期失效。
解决:过期时间加上一个随机因子

缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
View Code

 

184.redis 支持的数据类型有哪些?

string:最基本的数据类型,二进制安全的字符串,最大512M。
list:按照添加顺序保持顺序的字符串列表。
set:无序的字符串集合,不存在重复的元素。
sorted set:已排序的字符串集合。
hash:key-value对的一种集合。
bitmap:更细化的一种操作,以bit为单位。
hyperloglog:基于概率的数据结构。
View Code

 

185.redis 支持的 java 客户端都有哪些?

  Redis的Java客户端很多,官方推荐的有三种:Jedis、Redisson和lettuce。

186.jedis 和 redisson 有哪些区别?

Jedis:

轻量,简洁,便于集成和改造
支持连接池
支持pipelining、事务、LUA Scripting、Redis Sentinel、Redis Cluster
不支持读写分离,需要自己实现
文档差(真的很差,几乎没有……)
Redisson:

基于Netty实现,采用非阻塞IO,性能高
支持异步请求
支持连接池
支持pipelining、LUA Scripting、Redis Sentinel、Redis Cluster
不支持事务,官方建议以LUA Scripting代替事务
支持在Redis Cluster架构下使用pipelining
支持读写分离,支持读负载均衡,在主从复制和Redis Cluster架构下都可以使用
内建Tomcat Session Manager,为Tomcat 6/7/8提供了会话共享功能
可以与Spring Session集成,实现基于Redis的会话共享
文档较丰富,有中文文档
对于Jedis和Redisson的选择,同样应遵循前述的原理,尽管Jedis比起Redisson有各种各样的不足,但也应该在需要使用Redisson的高级特性时再选用Redisson,避免造成不必要的程序复杂度提升。
View Code

 

187.怎么保证缓存和数据库数据的一致性?

缓存和数据库数据不一样的原因:
    在分布式环境下,数据的读写都是并发的,对同一个数据进行读写,在数据库层面并发的读写并不能保证完成顺序,也就是说后发出的读请求很可能先完成(读出脏数据):
 (a)发生了写请求A,A的第一步淘汰了cache(如上图中的1)

(b)A的第二步写数据库,发出修改请求(如上图中的2)

(c)发生了读请求B,B的第一步读取cache,发现cache中是空的(如上图中的步骤3)

(d)B的第二步读取数据库,发出读取请求,此时A的第二步写数据还没完成,读出了一个脏数据放入cache(如上图中的步骤4)

即在数据库层面,后发出的请求4比先发出的请求2先完成了,读出了脏数据,脏数据又入了缓存,缓存与数据库中的数据不一致出现了

思路:
解决这个问题的关键是如何保证多个线程更新有序性,化并行为串行是解决这个问题的基本思路。

解决方法:
由于数据库层面的读写并发,引发的数据库与缓存数据不一致的问题(本质是后发生的读请求先返回了),可能通过两个小的改动解决:

(1)修改服务Service连接池,id取模选取服务连接,能够保证同一个数据的读写都落在同一个后端服务上

(2)修改数据库DB连接池,id取模选取DB连接,能够保证同一个数据的读写在数据库层面是串行的

 
View Code

 

188.redis 持久化有几种方式?

1.RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。
优势:
    整个Redis数据库将只包含一个文件,这样非常方便进行备份;
    RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快
    RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。
劣势:
    一旦发生故障停机, 你就可能会丢失好几分钟的数据;
    每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时

2.AOF持久化是将每一个收到的写命令都通过write函数追加到文件中。
优势:
    使用 AOF 持久化会让 Redis 变得非常耐久;
    AOF 文件有序地保存了对数据库执行的所有写入操作;
劣势:
    AOF 文件的体积通常要大于 RDB 文件的体积;
    根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 
View Code

 

189.redis 怎么实现分布式锁?

先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。
如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!
总的来说,执行上面的set()方法就只会导致两种结果:1. 当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。2. 已有锁存在,不做任何操作。
View Code

190.redis 分布式锁有什么缺陷?

1.单点问题
  一般都使用集群
2.锁过期后业务没有处理完成,有并发问题
    在时间上加一个随机值,使得过期时间分散一些。设置短的过期时间,启用一个线程更新过期时间,完成后停止线程。
View Code

191.redis 如何做内存优化?

1.降低Redis内存使用最直接的方式就是缩减键(key)和值(value)的长度。
key长度:如在设计键时,在完整描述业务情况下,键值越短越好。
value长度:值对象缩减比较复杂,常见需求是把业务对象序列化成二进制数组放入Redis。
注:高并发写入场景中,在条件允许的情况下建议字符串长度控制在39字节以内,减少创建redisObject内存分配次数从而提高性能。
2.共享对象池
对象共享池指Redis内部维护[0-9999]的整数对象池。创建大量的整数类型redisObject存在内存开销,每个redisObject内部结构至少占16字节,甚至超过了整数自身空间消耗。所以Redis内存维护一个[0-9999]的整数对象池,用于节约内存。 除了整数值对象,其他类型如list,hash,set,zset内部元素也可以使用整数对象池。因此开发中在满足需求的前提下,尽量使用整数对象以节省内存。
3.字符串优化
字符串对象是Redis内部最常用的数据类型。所有的键都是字符串类型, 值对象数据除了整数之外都使用字符串存储。
4.控制key的数量
当使用Redis存储大量数据时,通常会存在大量键,过多的键同样会消耗大量内存。
View Code

 

192.redis 淘汰策略有哪些?

1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。

 

2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种。

 

3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。

 

4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又做持久化存储的时候才用。不推荐

 

5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐

 

6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐
View Code

 

193.什么是分布式锁?有哪些分布式锁?

要介绍分布式锁,首先要提到与分布式锁相对应的是线程锁、进程锁。
线程锁:主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如synchronized是共享对象头,显示锁Lock是共享某个变量(state)。
进程锁:为了控制同一操作系统中多个进程访问某个共享资源,因为进程具有独立性,各个进程无法访问其他进程的资源,因此无法通过synchronized等线程锁实现进程锁。
分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。
View Code

194.分布式锁的实现有哪几种?

1.基于数据库实现的分布式锁
当我们想要锁住某个方法时,执行以下SQL: 
insert into methodLock(method_name,desc) values (‘method_name’,‘desc’) 
因为我们对method_name做了唯一性约束,这里如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,可以执行方法体内容。
当方法执行完毕之后,想要释放锁的话,需要执行以下Sql: 
delete from methodLock where method_name ='method_name'

优点:直接借助数据库,容易理解。
缺点:会有各种各样的问题,在解决问题的过程中会使整个方案变得越来越复杂。操作数据库需要一定的开销,性能问题需要考虑

2.基于缓存实现的分布式锁  redis
通过setnx()获取锁,并设置释放锁的过期时间。
优点:性能好。
缺点:过期时间设置多久合适(过短时间不够处理业务,过长等待时间就长)?因为业务处理时间过长或者full gc 的原因导致锁过期,其他线程获取锁有并发问题。单点问题。
方案:每获得一个锁时,只设置一个很短的超时时间,同时起一个线程在每次快要到超时时间时去刷新锁的超时时间。在释放锁的同时结束这个线程。如redis官方的分布式锁组件redisson,就是用的这种方案。  使用集群处理

3.基于Zookeeper实现的分布式锁

基于zookeeper临时有序节点可以实现的分布式锁。每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。
优点:有效的解决单点问题,不可重入问题,非阻塞问题以及锁无法释放的问题。实现起来较为简单。
缺点:性能上不如使用缓存实现分布式锁。 需要对ZK的原理有所了解。


原文:https://blog.csdn.net/u010963948/article/details/79006572 
版权声明:本文为博主原创文章,转载请附上博文链接!
View Code

193.redis 常见的性能问题有哪些?该如何解决?

1.Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化。   2.如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。   3.为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。
View Code

194.redisobject对象

 

Redis存储的所有值对象在内部定义为redisObject结构体,内部结构如下图所示。

Redis存储的数据都使用redisObject来封装,包括string,hash,list,set,zset在内的所有数据类型。理解redisObject对内存优化非常有帮助,下面针对每个字段做详细说明:

1.type字段:
表示当前对象使用的数据类型,Redis主要支持5种数据类型:string,hash,list,set,zset。可以使用type {key}命令查看对象所属类型,type命令返回的是值对象类型,键都是string类型。

2.encoding字段:
表示Redis内部编码类型,encoding在Redis内部使用,代表当前对象内部采用哪种数据结构实现。理解Redis内部编码方式对于优化内存非常重要 ,同一个对象采用不同的编码实现内存占用存在明显差异,具体细节见之后编码优化部分。

3.lru字段:
记录对象最后一次被访问的时间,当配置了 maxmemory和maxmemory-policy=volatile-lru | allkeys-lru 时, 用于辅助LRU算法删除键数据。可以使用object idletime {key}命令在不更新lru字段情况下查看当前键的空闲时间。

开发提示:可以使用scan + object idletime  命令批量查询哪些键长时间未被访问,找出长时间不访问的键进行清理降低内存占用。
4.refcount字段:
记录当前对象被引用的次数,用于通过引用次数回收内存,当refcount=0时,可以安全回收当前对象空间。使用object refcount {key}获取当前对象引用。当对象为整数且范围在[0-9999]时,Redis可以使用共享对象的方式来节省内存。具体细节见之后共享对象池部分。

5. *ptr字段:
与对象的数据内容相关,如果是整数直接存储数据,否则表示指向数据的指针。Redis在3.0之后对值对象是字符串且长度<=39字节的数据,内部编码为embstr类型,字符串sds和redisObject一起分配,从而只要一次内存操作。

开发提示:高并发写入场景中,在条件允许的情况下建议字符串长度控制在39字节以内,减少创建redisObject内存分配次数从而提高性能。
View Code

 

posted on 2019-03-11 10:59  nzxing  阅读(318)  评论(0编辑  收藏  举报

导航