关于java:Java面试问题汇总Redis

1.Redis 为什么是单线程? 为什么单线程还能这么快?

单线程可能防止线程切换和竞态产生的耗费,而且单线程能够简化数据结构和算法的实现
至于单线程还快,是因为Redis是基于内存的数据库内存响应速度是很快的,并且采纳epoll作为I/O多路复用技术,再加上Redis本身的事件处理模型将epoll中的连贯、读写、敞开都转换为事件不在网络I/O上节约过多工夫

epoll是为了解决Linux内核解决大量文件描述符提出的计划,属于Linux下多路I/O复用接口中select/poll的加强,常常用于Linux下高并发服务型程序,特地是大量并发连贯中只有少部分处于沉闷下的状况,能进步CPU利用率。
epoll采纳事件驱动,只须要遍历那些被内核IO事件异步唤醒之后退出到就绪队列并返回到用户空间的描述符汇合
epoll提供两种触发模式,程度触发(LT)和边际触发(ET),目前效率最高的IO操作计划是:epoll+ET+非阻塞IO模型

2.Redis 应用场景

最多的利用于缓存,其余能够用于排行榜、计数器、音讯队列等

3.Redis 淘汰策略

Redis 3.0 版本反对的策略

  1. volatile-lru:从设置过期工夫的数据集(server.db[i].expires)中挑选出最近起码应用的数据淘汰。没有设置过期工夫的key不会被淘汰,这样就能够在减少内存空间的同时保障须要长久化的数据不会失落
  2. volatile-ttl:除了淘汰机制采纳LRU,策略基本上与volatile-lru类似,从设置过期工夫的数据集(server.db[i].expires)中筛选将要过期的数据淘汰ttl值越大越优先被淘汰
  3. volatile-random:从已设置过期工夫的数据集(server.db[i].expires)中任意抉择数据淘汰。当内存达到限度无奈写入非过期工夫的数据集时,能够通过该淘汰策略在主键空间中随机移除某个key。
  4. allkeys-lru:从数据集(server.db[i].dict)中筛选最近起码应用的数据淘汰,该策略要淘汰的key面向的是整体key汇合,而非过期的key汇合。
  5. allkeys-random:从数据集(server.db[i].dict)中抉择任意数据淘汰
  6. no-enviction:禁止驱赶数据,也就是当内存不足以包容新入数据时,新写入操作就会报错,申请能够持续进行,线上工作也不能继续进行,采纳no-enviction策略能够保证数据不被失落,这也是零碎默认的一种淘汰策略。

4.Redis 长久化机制

1.RDB长久化:

以后过程数据生成快照保留到硬盘的过程,触发形式有手动触发主动触发
手动触发命令:save和bgsave命令

  • save命令阻塞以后Redis服务,直到RDB过程实现为止不倡议线上环境应用
  • bgsave命令Redis过程执行fork操作创立子过程,RDB长久化过程由子过程负责,实现后主动完结,阻塞只产生在fork阶段,个别工夫很短
    主动触发:
  • 应用save相干配置,如”save m n”。示意m秒内数据集存在n次批改时,主动触发
  • 如果从节点执行全量复制操作,主节点主动执行bgsave 生成RDB文件并发送给从节点
  • 执行debug reload命令从新加载Redis时,也会主动触发save操作
  • 默认状况下执行shutdown命令时,如果没有开始AOF长久化则主动执行bgsave

RDB文件保留在dir配置指定的目录下,文件名通过dbfilename配置指定,能够通过执行config set dir {newDir} 和 config set dbfilename {newFileName} 运行期动静执行,当下次运行时RDB文件会保留到新目录

  • 遇到磁盘损坏或写满时,能够通过config set dir 在线批改文件门路到可用的磁盘门路,之后执行bgsave进行磁盘切换
  • redis默认采纳LZF算法对生成的RDB文件进行压缩,压缩后文件元小于内存大小,默认开启,能够通过参数config set rdbcompression {yes|no}动静批改

RDB长处:

  • RDB是一个紧凑的二进制文件,代表Redis在某个工夫点上的数据快照适宜备份,全量复制等场景
  • Redis加载RDB复原数据远远于AOF形式

RDB毛病:

  • 没方法做到实时长久化/秒级长久化
  • RDB文件应用特定二进制保留,存在兼容问题

2.AOF长久化

独立日志的形式记录每次写命令,写入的内容间接是文本协定格局重启时再从新执行AOF文件中的命令达到复原数据的目标,解决了数据长久化实时性问题
开启AOF:appendonly yes,默认不开启
AOF文件名: appendfilename 配置,默认appendonly.aof
保留门路:同RDB,通过dir配置

AOF工作流程:

  1. 所有的写入命令追加到aof_buf(缓冲区)
  2. AOF缓冲区依据对应的策略向硬盘做同步操作
  3. 随着AOF文件越来越大,须要定期对AOF文件进行重写,压缩,父过程执行fork创立子过程,由子过程依据内存快照执行AOF重写,父进行持续响应前面的命令,在子过程实现重写后,父过程再把新增的写入命令写入到新的AOF文件中
  4. Redis服务重启加载AOF文件进行数据恢复

AOF为什么间接采纳文本协定格局?

  • 文本协定具备良好的兼容性
  • 开启AOF后,所有写入命令都蕴含追加操作,间接采纳协定格局,防止二次解决开销
  • 文本协定具备可读性,不便间接批改和解决

AOF 为什么把命令追加到aof_buf中

  • 写入缓存区aof_buf中,能进步性能,并且Redis提供了多种缓存区同步硬盘策略

重写后的AOF文件为什么能够变小?

  • 过程内曾经超时的数据不再写入文件
  • 旧的AOF文件含有有效命令,重写应用过程内数据间接生成,新的AOF文件只保留最终数据的写入命令
  • 多条写命令能够合并为一个,为了避免溢出,以64个元素为界拆分为多条

AOF缓冲区同步策略,通过参数appendfsync管制

可配置值 阐明 其余
always 命令写入aof_buf后调用零碎fsync操作同步到AOF文件,fsync实现后线程返回 每次写入都要同步AOF文件,在个别的SATA硬盘很难达到高性能
everysec 命令写入aof_buf后调用零碎write操作,write实现后线程返回。fsync同步操作由线程每秒调用一次(倡议策略) 默认同步策略
no 命令写入aof_buf后调用零碎write操作,不对AOF文件做fsync同步,同步硬盘操作由操作系统负责,通常同步周期最长30秒 操作系统每次同步AOF文件的周期不可控,而且会加大每次同步硬盘的数据量,尽管晋升了性能,但数据安全性无奈保障

AOF手动触发:调用bgrewriteaof命令
主动触发:有两个参数

  • auto-aof-rewrite-min-size: 示意运行时AOF重写时文件最小体积,默认64MB
  • auto-aof-rewrite-percentage: 代表以后AOF文件空间(aof_current_size)和上一次重写后AOF文件空间(aof_base_size) 的比值
    主动触发机会=aof_current_size > auto-aof-rewrite-min-size && (aof_current_size -aof_base_size) / aof_base_size >= auto-aof-rewrite-percentage

RDB和AOF同时开启,并且AOF文件存在,优先加载AOF文件

AOF文件谬误,能够通过redis-check-aof-fix 修复

3.Redis数据类型

字符串
key是字符串类型,字符串类型的值能够是字符串、数字、二进制(最大不能超过512MB),是动静字符串,外部通过预调配冗余空间的形式来缩小内存的频繁调配

命令 解释 备注
set 设置值 key
value
[ex seconds] 为键设置秒级过期工夫
[px milliseconds] 为键设置毫秒级别的过期工夫
[nx\xx] nx键必须不存在才能够设置胜利 xx键必须存在才能够设置胜利
mset 批量设置值 key
value
[key value …]
mget 批量获取值 key
[key …]
incr 对值做自增操作 值不是整数,返回谬误
值是整数,返回自增后的后果
键不存在,依照值为0自增,返回后果为1
decr 自减 key
incrby 自增指定数字 key increment
decrby 自减指定数字 key decrement
incrbyfloat 自增浮点数 key increment
append 追加值 key value
strlen 字符串长度 key
getset 设置并返回原值 key value
setrange 设置指定地位的字符 key offeset value
getrange 获取局部字符串 key start end

哈希
一个键值对构造, 内部结构同Java的 HashMap, 数组+链表的构造,值只能存储字符串,编码是ziplist或者hashtable。另外在rehash的时候,采纳定时工作渐进式迁徙内容

命令 解释 备注
hset 设置值 key field value
hsetnx 相似setnx
hget 获取值 key field
hdel 删除一个或多个field key field [field …]
hlen 计算field个数
hmget 批量获取 key field [field …]
hmset 批量设置 key field value [field value …]
hexists 判断field是否存在 key field
hkeys 获取所有field key
hvals 获取所有value key
hgetall 获取所有的field-value key
如果元素很多,会造成阻塞,倡议应用hscan
hincrby hincrbyfloat 相似incrby / incrbyfloat key field increment
hstrlen 计算value字符串长度 key field

列表
用来存储多个有序的字符串,一个列表最多能够存储232 – 1 个元素,列表中的元素能够是反复的,相当于Java中的LinkedList, 是一个链表而不是数组, 底层是采纳quicklist构造,在数据量少的时候会应用ziplist压缩列表,数据量多的时候才应用quicklist

命令 解释 备注
rpush 从左边插入元素 key value [value …]
lpush 从右边插入元素 key value [value …]
linsert 向某个元素前或者后插入元素 key before\ after pivot value
从列表中找到等于pivot元素,在其前before 或者after 插入一个新的元素value
lrange 获取指定范畴内的元素列表 key start end
索引下标从左到右是0到N-1, 从右到左是-1到-N; end蕴含本身
lindex 获取列表指定索引下标的元素 key index
llen 获取列表长度 key
lpop 从列表左侧弹出元素 key
rpop 从列表右侧弹出元素 key
lrem 删除指定元素 key count value
从列表中找到等于value的元素进行删除,count>0, 从左到右,删除最多count个元素。count<0, 从右到左,删除最多count绝对值个元素。count=0,删除所有
ltrim 依照索引范畴修剪列表 key start end
lset 批改指定索引下标的元素 key index newValue
blpop
brpop
弹出元素阻塞版 key [key …] 多个键
timeount 阻塞工夫(秒)

汇合(set)
用来保留多个的字符串的元素,但和列表元素不一样的是,汇合中不容许有反复元素,并且汇合中的元素是无序的,不能通过索引下标获取元素,一个汇合最多能够存储232 – 1个元素
当汇合元素都是整数并且元素个数小于set-max-intset-entries配置(默认512个)时,应用intset编码缩小内存应用,否则应用hashtable编码
相当于Java中的HashSet, 所有的value都是一个值NULL

命令 解释 备注
sadd 增加元素 key element [element …]
srem 删除元素 key element [element …]
scard 计算元素个数 key
sismember 判断元素是否在汇合中 key element
srandmember 随机从汇合返回指定个数元素 key [count]
spop 从汇合随机弹出元素,会删除元素 key [count]
smembers 获取所有元素 key
sinter 求多个汇合的交加 key [key …]
sunion 求多个汇合的并集 key [key …]
sdiff 求多个汇合的差集 key [key …]
sinterstore
suionstore
sdiffstore
将交加、并集、差集的后果保留 destination key [key…]
destination 指标汇合名称
汇合间的运算在元素比拟多的状况下会比拟耗时

有序汇合
汇合不能反复,但汇合中的元素能够依据score分数排序,排序从小到大
当有序汇合的元素个数小于zset-max-ziplist-entries(默认128个),同时每个元素的值都小于zset-max-ziplist-value配置(默认64字节)时,应用ziplist来作为外部编码,否则应用skiplist作为外部编码

其相当于Java的SortedSet 和 HashMap结合体,外部实现’跳跃表‘ 的数据结构

skiplist 编码的有序汇合对象应用zset构造做为底层实现,zset构造包含一个字典和一个跳跃表(依据成员查找分值和范畴操作的效率最高)

命令 解释 备注
zadd 增加成员 key [NX\XX] [CH] [INCR]score member [score member …]
[NX\XX] NX: 只有在元素不存在时候增加新元素; XX:更新曾经存在的元素,不增加元素
CH 返回有序汇合元素和分数发生变化的个数 incr 对score做减少,相当于zincrby
zcard 计算成员个数 key
zscore 计算某个成员的分数 key member
zrank 计算成员排名,分数从低到高 key member
zrevrank 计算成员排名,分数从高到低 key member
zrem 删除成员 key member [member …]
zincrby 减少成员分数 key increment member
zrange
zrevrange
返回指定排名范畴的成员 key start end [withscores] 显示分数
zrangebyscore
zrevrangebyscore
返回指定分数范畴的成员 key min max [withscores] [limit offset count]
key max min [withscores] [limit offset count]
min和max 反对开区间(小括号) 和闭区间(中括号),-inf 和 +inf 无限小和有限大士大
zcount 返回指定分数范畴成员个数 key min max
zremrangebyrank 删除指定排名内的升序元素 key start end
zremrangebyscore 删除指定分数范畴的成员 key min max
zinterstore 交加 destination numkeys key [key …] [weights weight [weight …]] [aggregate sum\min\max]
destination 交加计算结果保留到这个键
numkeys 须要做交加计算键的个数
key[key…] 须要做交加计算的键
weights weight[weight…] 每个键的权重,在做交加计算时,每个键中的每个member会将本人分数乘以这个权重,每个键的权重默认为1
aggregate sum\min\max 计算成员交加后,分值能够依照sum、min、max 做汇总,默认是sum
zunionstroe 并集 参数同zinterstore

4.Pipeline

Pipeline(流水线) 机制将一组Redis命令进行组装,通过一次RTT传输给Redis,再将这组Redis命令的执行后果依照程序返回给客户端

原生批量命令与Pipeline比照:

  • 原生批量命令是原子的,Pipeline是非原子
  • 原生批量命令是一个命令对应多个key,Pipeline反对多个命令
  • 原生批量命令是Redis服务端反对实现的,而Pipeline须要服务端和客户端独特实现

5.事务

redis 简略事务性能,将一组须要一起执行的命令放到multi和exec两个命令之间。multi命令代表事务开始,exec命令代表事务完结,他们之间的命令是原子程序执行的。如果要进行事务的执行用discard命令代替exec命令

watch 命令 用来确保事务中的key没有被其余客户端批改过,才执行事务,否则不执行(相似乐观锁

6.Lua

两种办法:

  • eval
    eval 脚本内容 key个数 key列表 参数列表

     eval 'return "helo "..KEYS[1].. ARGV[1]' 1 redis world

还能够应用redis-cli –eval 间接执行文件

  • evalsha
    evalsha 脚本sha1值 key个数 key列表 参数列表
    将Lua脚本加载到Redis服务端,失去该脚本的SHA1校验和,evalsha应用SHA1作为参数执行对应的Lua脚本,防止每次发送脚本,脚本常驻与内存中

加载脚本到Redis: redis-cli script load “$(cat ~/lua_test.lua)”

7.Redis键过期删除策略

键过期,外部保留在过期字典expires中,Redis采纳惰性删除定时工作删除机制;

惰性删除用于在客户端读取带有超时属性的键时,如果曾经超过设置的过期工夫,会执行删除并返回空,但这种形式如果键过期,而且始终没有被从新拜访,键始终存在
定时工作删除:可能解决惰性删除问题,Redis外部保护一个定时工作,每秒运行10次。依据键的过期比例,应用快慢两种速率模式回收键,毛病是占用CPU工夫,在过期键多的时候会影响服务器的响应工夫和吞吐量

定期删除:每隔一段时间执行一次删除过期键操作,通过限度删除操作的执行时长和频率来缩小影响,毛病须要正当设置执行时长和频率

生成RDB文件的时候已过期的键不会被保留,在载入RDB文件的时候,主服务器会对键进行查看,过期的键不会被载入,而从服务会将所有键载入,直到主服务来删除告诉

AOF写入的时候,如果某个键过期,会向AOF追加一条DEL命令;AOF重写的时候会对键进行查看,过期键不会被写入

复制的时候,主服务器发送删除告诉,从服务器接到删除告诉时才删除过期键

8.Redis高可用计划

  1. 主从模式:一主二从
    配置redis.conf , 从节点配置 slaveof 127.0.0.1 6379
    确认主从关系: redis-cli -h 127.0.0.1 -p 6379 info replication
  2. 哨兵模式:
    配置 redis-sentinel.conf ,sentinel monitor mymaster 127.0.0.1 6379 1
    最初一位是选举master须要的票数
    启动哨兵:
    redis-sentinel redis-sentinel.conf
    或者 redis-server redis-sentinel.conf –sentinel
  3. redis-cluster:
    每个节点保留数据和整个集群状态,每个节点都和其余节点连贯。采纳哈希函数把数据映射到一个固定范畴的整数汇合中,整数定义为槽。所有键依据哈希函数映射到0~16383整数槽内,公式 slot = CRC16(key) & 16383
  4. Codis
  • 集群限度:
  1. key批量操作反对限度,目前只反对具备雷同slot值的key执行批量操作
  2. key事务操作反对无限
  3. key作为数据分区最小粒度,不能将大的键值对象hash、list等映射到不同节点
  4. 只能应用0号数据库
  5. 从节点只能复制主节点,不反对嵌套树状复制构造
  • 主从复制过程

    1. 从节点执行slaveof后,从节点保留主节点地址信息
    2. 从节点外部通过每秒运行的定时工作保护复制相干逻辑,当定时工作发现存在新的主节点后,会尝试与该节点建设网络连接
    3. 连贯建设胜利后,从节点发送ping申请进行首次通信,目标是检测主从之间网络套接字是否可用,主节点以后是否承受解决命令
    4. 如果主节点配置了明码验证,则从节点必须要配置雷同的明码能力通过验证,进行复制同步
    5. 通过验证后,主从可失常通信了,主节点会把数据继续发给从节点,同步形式有全量同步和局部同步,刚建设建设的时候,会进行全量同步,同步完结后,进行局部同步
    6. 当主节点与从节点同步完以后的数据后,主节点会把后续新增的命令继续发送给从节点进行同步
  • 哨兵模式 最小配置 1主 2从 3哨兵,3个哨兵能监控每个master和salve

9.缓存问题

  • 缓存穿透

    缓存穿透是指,缓存中不存在该key的数据,于是就是去数据库中查问,数据库也不存在该数据,导致循环查问数据

    优化:

    1. 缓存空对象

      对于不存在的数据,仍旧将空值缓存起来。但这会造成内存空间的节约,能够针对这类数据加一个过期工夫。对于缓存和存储层数据的一致性,能够在过期的时候,申请存储层,或者通过音讯零碎更新缓存

    2. 布隆过滤器

      将所有存在的数据做成布隆过滤器,能够应用Bitmaps实现,其在大数据量下空间占用小。当有新的申请时,先到布隆过滤器中查问是否存在,如果不存在该条数据间接返回;如果存在该条数据再查问缓存、查询数据库。

      Redis4.0以上采纳插件集成,https://github.com/RedisBloom…

      原理:

      在redis中是一个大型的位数组,通过计算key的hash而后对数组长度取模失去一个地位,进行写入;在判断是否存在时,判断位数组中几个地位是否都为1,只有一个位为0,就阐明这个key不存在。布隆过滤器也会存在肯定的误判,如果位数组比拟稠密,概率就会很大,否则就会升高。

  • 缓存击穿

    缓存击穿指,某key忽然变成了热点key,大量申请到该key,但key刚好又生效,导致从数据库中去查问数据

    优化:

    ​ 通过互斥锁形式,在申请数据库之前设置setnx,在查问完数据库,并更新缓存后,删除setnx

  • 缓存雪崩

    指缓存因为大量申请,造成缓存挂掉,大量申请间接打到存储层,造成存储层挂机

    优化:

    1. 应用主从,哨兵,集群模式保障缓存高可用
    2. 依赖隔离组件为后端限流并降级
    3. 提前演练,做好后备计划
  • 缓存更新形式

    同步更新,先写入数据库,写入胜利后,再更新缓存。

    异步更新,通过消息中间件进行触发更新。

    生效更新,在取不到缓存的时候,从数据库取数据,再更新缓存。

    定时更新,通过定时工作来更新缓存

  • 缓存不统一

    通过减少重试机制,弥补工作,达到最终统一

  • 热点key重建

    优化

    1. 应用互斥锁,只容许一个线程重建缓存
    2. 应用逻辑缓存过期,在value中存一个key过期工夫,在获取key的时候通过逻辑工夫进行判断

10.Redis 锁的实现 和 Zookeeper锁实现区别

Redis 锁 通过 setnx key value 或者 set key value px millseconds nx 当返回1时代表获取到锁,返回0示意获取锁失败,通过Redis的key超时机制来开释锁

PS: Redis锁可能会在业务逻辑还没执行完的时候就曾经超时开释,因而在开释锁的时候,可能其余线程曾经从新持有了该锁,所以要在开释锁的时候验证key对应的value值,在创立缓存的时候,value值是随机生成的。或者应用redisson做为分布式锁

ZK锁 通过在服务端新建一个长期有序节点,哪个客户端胜利创立了第一个长期有序节点,就代表该客户端取得了锁,前面节点的客户端会处于监听状态,当开释锁的时候,服务端就会删除第一个长期节点,此时第二个长期节点能监听到上一个节点的开释事件,这样第二个节点就变成第一个节点,此时客户端2就代表取得了锁。如果客户端的会话敞开,长期节点会被删除,也就开释了锁

《三种分布式锁的优缺点及解决方案》

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理