redis常见问题

redis为什么这么快

redis是基于内存的采用的是单进程单线程模型的KV数据库,使用多路I/O复用模型。

redis的线程模型

内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的,所以redis是单线程的。采用IO多路复用机制同时监听多个Socket,根据Socket上的事件来选择对应的事件处理器进行处理。

redis数据结构

基本:string(缓存,计数器,共享session),list(播放列表,文章列表,消息队列)hash(结构化数据),set(共同好友),sortedset(排行榜)
高级:bitmap,HyperLogLog,geo,pub/sub

常用命令

key相关

keys *
dbsize
type key
exists key [key ...]
del key [key ...]
move key db
ttl key(秒) / pttl key(毫秒)
expire key seconds / pexpire key milliseconds
persist key 
rename key newkey

string相关

set key value [EX seconds] [PX milliseconds] [NX|XX]
get key
incr key (数字) / incrby / decr / decrby
mset key value [key value ...] # mset java1 1 java2 2 java3 3
mget key [key ...] #  mget java1 java2
strlen key # 获取值长度
append key value # 追加 原:test:aaa  append key test bbb 新:test:aaabbb
getrange key start end # 获取部分字符

set相关

sadd key member [member ...]  # 存储值 sadd test java php c++ go python java
smembers key # 获取值 smembers test (java php c++ go python)
srandmember key count # 随机获取值 srandmember test 3
sismember key member # 判断值是否存在 sismember test go
scard key # 获取集合元素个数 (5个)
srem key member [member ...] # 删除集合元素 
spop key [count] # 弹出元素

sortedset相关

zadd key score member  # 存储值 zadd test 100 a 200 b
zscore key member # 获取元素分数 zscore test b (200)
zrange key start stop [WITHSCORES] # 获取排名范围排名 zrange test 0 -1 WITHSCORES (a,100,b,200)
zrangebyscore key min max [WITHSCORES] [LIMIT offset count] # 获取指定分数范围排名
# zrangebyscore test 100 150 WITHSCORES limit 0 1 (a,100)
zcard key # 获取集合元素个数 (2)
zincrby key increment member # 增加指定元素分数 zincrby test 300 b (500)
zcount key min max # 获取指定范围分数个数
zrem key member # 删除指定元素
zrank key member # 获取排名,从0开始

list相关

lpush key value [value ...] # 左端存值
rpush key value [value ...] # 右端存值
lset key index value # 索引存值 (会覆盖原值)
lrange key start stop # 获取列表元素 
lpop key # 左端弹出
rpop key # 右端弹出
llen key # 长度
lindex key index # 索引获取值(index为负时代表从右边-1开始索引)
lrem key count value # 删除元素
ltrim key start stop # 范围删除(只保留start到stop的值)

hash相关

hset key field value  [field value ...] # 存放键值 hset user name wghdr age 26
hsetnx key field value # key不存在时 hset users name wghdrr
hget key field # 获取字段值 hget user name
hmget key field [field ...] # 获取多个值 hmget user name age
hgetall key # 获取所有 
hkeys key # 获取所有key的字段
hvals key # 获取所有值
hexists key field # 判断字段是否存在
hlen key # 获取字段数量
hdel key field [field ...] # 删除key的字段 hdel user age

线上keys命令

keys命令会导致线程阻塞一会,直到命令执行完成后,业务才能恢复。
使用scan命令,可以不用阻塞主线程,并支持游标按批次迭代返回数据。返回的数据有可能重复,需要在业务层按需要去重。

scan 0 match wp_wp:posts* count 1000

redis持久化

  • RDB:对redis中的数据执行周期性的持久化。

    • 优点:
      RDB对redis的性能影响非常小,是因为在同步数据的时候他只是fork了一个子进程去做持久化的,更适合做冷备,恢复速度快。
    • 缺点:
      RDB都是快照文件,都是默认五分钟甚至更久的时间才会生成一次,可能会丢失这5分钟的数据。
      在生成RDB文件时,如果文件很大,会会间断性暂停服务。
  • AOF:对每条写入命令作为日志,以append-only追加的模式写入一个日志文件中。

    • 优点:
      追加模式减少了磁盘开销,AOF是一秒一次去通过一个后台的线程fsync操作,最多丢一秒的数据。AOF更适合做热备。
    • 缺点:
      一样的数据,AOF文件比RDB还要大。
      AOF开启后,Redis支持写的QPS会比RDB支持写的要低。
  • 选择
    两种机制全部开启的时候,Redis在重启的时候会默认使用AOF去重新构建数据,因为AOF的数据是比RDB更完整的。

redis集群模式

启动多个redis-server实例,指定一个或者多个为master(奇数个),其他的为slave。比如一主两从,三主三从。集群启动后,数据写入到master上,slave只读,数据自动同步到slave上。缺点时出现故障无法自动恢复。

redis哨兵模式

哨兵会实时监控master和slave的状态,当master出现故障,会选择一个slave切换为master,并修改其他slave的配置文件指向新的master,还可以发送消息通知管理员。

哨兵模式故障转移流程

  1. 当一个sentinel发现master下线,它会将下线的master确认为主观下线。
  2. 当“法定个数”(quorum)sentinel确认该master节点下线,那么sentinel会将其确认为客观下线。
  3. sentinel发起选举,选举出一个sentinel作为leader,由它去进行故障转移,将原来连接已客观下线master最优的一个slave提升为新master。老的master如果重新上线,它将被降级为slave。

sentinel节点个数最好 >= 3,且为奇数,quorum为sentinel节点个数的一半+1。比如sentinel为3,quorum为2。这样不会出现两个票数一样的 leader同时被选上。

哨兵脑裂

当master断开连接,sentinel选择了一个slave为新的master,这时就出现了2个master。但是client仍然往老的master写入数据,这部分数据就会丢失。

解决

# master 须要有至少 x 个副本连接。
min-slaves-to-write x
# 数据复制和同步的延迟不能超过 x 秒。
min-slaves-max-lag x

redis的同步机制

redis可以使用主从同步,从从同步。第一次同步时,master做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将RDB文件全量同步到slave,slave接收完成后将RDB镜像写到磁盘,然后加载到内存。加载完成后,再通知master将期间修改的操作记录同步到slave进行重放就完成了同步过程。后续的增量数据通过AOF日志同步即可,有点类似数据库的binlog。

redis key的过期策略

是有定期删除,惰性删除,内存淘汰三种。

  • 定期删除:默认100s就随机抽一些设置了过期时间的key,去检查是否过期,过期了就删了。
  • 惰性删除:我等你来查询了我看看你过期没,过期就删了还不给你返回,没过期就直接返回。
  • 内存淘汰:定期没删,也没有查询这个key的情况。
    • noeviction:当内存达到限制时返回错误。
    • allkeys-lru: 尝试回收最少使用的键(LRU).
    • volatile-lru: 尝试回收最少使用的键(LRU),但仅限于过期的键.
    • allkeys-random: 回收随机的键.
    • volatile-random: 回收随机的键,但仅限于过期的键。
    • volatile-ttl: 回收过期的键,并且优先回收存活时间(TTL)较短的键。

redis+db读写模式

读的时候先读缓存,缓存没有的话,就去读数据库,读取后写入到缓存,再返回给客户端。
更新的时候,先更新数据库,再删除缓存。

为什么是删除缓存

因为数据可能更新的很频繁,但是缓存访问的不频繁,有大量的冷数据,用到缓存时才去对缓存做修改。

数据不一致怎么处理

缓存不一致产生的原因一般是主动更新失败,例如更新db后,更新redis因为网络原因请求超时;或者是异步更新失败导致。

解决的办法是,如果服务对耗时不是特别敏感可以增加重试;如果服务对耗时敏感可以通过异步补偿任务来处理失败的更新,或者短期的数据不一致不会影响业务,那么只要下次更新时可以成功,能保证最终一致性就可以。

缓存穿透,缓存击穿,缓存雪崩

  • 缓存穿透:用户不断的用缓存和db中不存在的数据发起请求,导致db压力过大,严重会导致db挂掉。

    • 解决:
      1.增加参数校验
      2.缓存一个空对象,对不存在的请求直接返回空对象,这样缓存中可能有大量空对象。
      3.在接入或者网关层增加对频繁访问ip的限制
      4.增加布隆过滤器。原理就是判断请求的key是否存在,如果存在就去db查询刷新缓存再返回结果,如果key不存在,那么数据一定不存在,直接返回为空。
  • 缓存击穿:有一个key非常热点一直有大量的请求访问,当这个key失效的瞬间,大量针对这个数据的请求会击穿缓存,直接访问db。

    • 解决:
      1.设置热点key用不过期。
      2.使用互斥锁更新,保证同一个进程中针对同一个数据不会并发请求到db,减小db压力。
  • 缓存雪崩:大量缓存失效或者缓存挂了,直接请求到db。

    • 解决:
      1.设置热点key随机过期,避免统一时间大量key过期。
      2.使用集群部署,将热点key分布到多个实例上
      3.设置热点key用不过期。

redis和memcached的区别

redis支持更多的数据结构
redis支持集群模式,memcached不支持,需要依靠客户端去往分片中写入数据。
memcached可以使用多核,在存储大数据上性能更高。

在集群模式下,redis的Key是如何寻址的

hash,一致性hash,hash slot算法。

如何保证redis中的数据都是热点数据

可以使用redis的allkeys-lru淘汰策略来实现,从redis的数据中挑选最近最少使用的数据删除,这样频繁被访问的数据就可以保留下来了。

0 0 投票数
文章评分
订阅评论
提醒
guest

0 评论
内联反馈
查看所有评论

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部
0
希望看到您的想法,请您发表评论x