Redis 知识点梳理

75 阅读8分钟

什么是 Redis?

首先看一下官方的介绍:Redis 是一款开源的,基于内存的数据结构存储,可用作数据库,缓存和消息代理。支持的数据结构有:字符串(strings),哈希(hashes),列表(lists),集合(sets),有序集合(sorted sets)等,带有范围查询的排序集合,位图,超级日志,带有半径查询和流的地理空间索引。Redis 内置了副本,Lua 脚本,LRU 算法,事务和不同级别的磁盘持久化功能,并通过 Redis Sentinel 和 Redis Cluster 自动分区来确保高可用性。

官方的介绍基本上已经把 Redis 的所有卖点都介绍了,对于后端开发人员而言,Redis 基本是最常用的工具之一,而且也是面试必问题目。今天我们就来撸一遍 Redis 的常见知识点。

安装 Redis

下载及安装

我直接在官网上下载最新稳定版 - 6.0.6, 下载到本地后解压

$ cd redis-6.0.6
$ make
$ sudo make install

修改配置信息

由于redis默认不会以守护进程方式启动,因此我们需要修改配置

$ vim redis.conf

找到daemonize no改为deamonize yes

启动 Redis

进入/usr/local/bin目录下,先启动Server端,看到这个经典的欢迎界面就说明成功了。

$ cd /usr/local/bin
$ redis-server

4395:C 27 Jul 2020 23:43:00.640 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
4395:C 27 Jul 2020 23:43:00.640 # Redis version=6.0.6, bits=64, commit=00000000, modified=0, pid=4395, just started
4395:C 27 Jul 2020 23:43:00.640 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
4395:M 27 Jul 2020 23:43:00.641 * Increased maximum number of open files to 10032 (it was originally set to 256).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.0.6 (00000000/0) 64 bit
  .-`` .-```.  ```/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 4395
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

4395:M 27 Jul 2020 23:43:00.642 # Server initialized
4395:M 27 Jul 2020 23:43:00.642 * Ready to accept connections

然后再启动客户端:

$ redis-cli
127.0.0.1:6379> ping
PONG

使用 Redis 有哪些好处?

1.速度快,由于数据存在内存中,而且是基于 key 哈希值进行存储,因此查询和操作时间复杂度都是 O(1)。另外 Redis 是单线程,不会有线程切换的性能损耗。

2.相比 memcache 提供了更加丰富的数据类型支持,如’string’,’hash’, ‘list’,‘set’,‘sorted set’。

3.可以利用 Redis 实现分布式锁机制。

4.丰富的特性,可用于热点数据缓存,计数器,LRU 等功能。

什么样的场景使用 Redis?

1.热点数据,不经常修改的数据可以使用redis进行缓存处理,减轻数据库查询压力。

2.分布式锁。

3.SSO Session, Token。

4.排行榜,计数器

5.消息队列(Pub/Subs)

如何使用 Redis 实现分布式锁?

实现分布式锁主要利用 Jedis 中 setnx 的 API。SETNX key value。SETNXSet if not exists的缩写,当 key 已经存在了,则存储不成功,返回0。当 key不存在时,操作成功,返回1。

如下是一个 schedule Job 分布式锁的 Java 实现:

public boolean isLocked(String locker, int expire) {
    Jedis jedis = null;
    try {
      jedis = jedisPool.getResource();
      if (jedis.setnx(locker, "LOCKED") == 1) {
        jedis.expire(locker, expire);
        return true;
      }
    } catch (Exception e) {
      throw new RuntimeException("Locking error", e);
    } finally {
      if (jedis != null) {
        jedis.close();
      }
    }
    return false;
  }

Redis 的过期策略和内存淘汰机制

Redis采用定期删除 + 惰性删除策略。首先要明白定时删除策略是要有定时器来监视所有 key,对过期数据及时删除,虽然内存得到及时释放,但十分消耗 CPU 资源。在大并发请求下,Redis 应将 CPU 多用在处理请求上,而不是去删除 Key。因此 Redis 优化策略,每100ms抽样检查一下过期 Key,有则删除。借此,Redis 可以一定程度上减轻内存的压力,但肯定还会有很多 Key 被遗漏掉,这时再使用惰性删除机制来进行清理。也就是说当去获取 Key 时,Redis 先检查 Key 是否过期,如果过期就会将数据删除。如果定期删除和惰性删除的策略无法及时清理出内存空间,Redis 是不是就会爆仓了?不会,因为 Redis 中有内存淘汰机制。一旦数据爆仓,Redis 会优先清除掉最近最少用的 key,以此来保证 Redis 的高命中,高可用。

•noeviction 当内存不足以容纳新写入数据时,新写入操作会报错。

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

•volatile-ttl 当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。

•volatile-random 当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。

•allkeys-lru 当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key。

•allkeys-random 当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。

缓存的穿透和雪崩问题

就我过去的项目经验还没有遇到过这两种问题,一般流量要几百万左右才可能出现。

穿透 即网站遭受到恶意攻击,不断请求数据库中不存在的数据,由于无法击中缓存,因此所有请求都会到达数据库,最终数据库不堪压力而连接异常。 解决方案如下:

•利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试。

•采用异步更新策略,无论 Key 是否取到值,都直接返回。Value 值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。

•提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的 Key。迅速判断出,请求所携带的 Key 是否合法有效。如果不合法,则直接返回。

雪崩 即缓存同一时间大面积的失效,这个时候会有大量请求同时到达数据库,导致数据库连接异常。

•给缓存的失效时间,加上一个随机值,避免集体失效。

•互斥锁,但该方案吞吐量明显下降。

Redis 几种集群模式

•单机模式(Standalone), 最基本的部署方式。

•主从复制(Master-Slave Replication),这是Redis最普遍和基础的集群模式,其工作原理:Slave从节点服务启动并连接到Master之后,它将主动发送一个SYNC命令。Master服务主节点收到同步命令后将启动后台存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕后,Master将传送整个数据库文件到Slave,以完成一次完全同步。而Slave从节点服务在接收到数据库文件数据之后将其存盘并加载到内存中。此后,Master主节点继续将所有已经收集到的修改命令,和新的修改命令依次传送给Slaves,Slave将在本次执行这些数据修改命令,从而达到最终的数据同步。 如果Master和Slave之间的链接出现断连现象,Slave可以自动重连Master,但是在连接成功之后,一次完全同步将被自动执行。

•哨兵模式(Sentinel)是从Redis的2.6版本开始提供的,但是当时这个版本的模式是不稳定的,直到Redis的2.8版本以后,这个哨兵模式才稳定下来,无论是主从模式,还是哨兵模式,这两个模式都有一个问题,不能水平扩容,并且这两个模式的高可用特性都会受到Master主节点内存的限制。 Sentinel(哨兵)进程是用于监控redis集群中Master主服务器工作的状态,在Master主服务器发生故障的时候,可以实现Master和Slave服务器的切换,保证系统的高可用。

•Jedis sharding和利用中间件代理,将我们需要存入redis中的数据的key通过一套算法计算得出一个值。然后根据这个值找到对应的redis节点,将这些数据存在这个redis的节点中。但这两种集群方式会牺牲性能。但相对主从复制模式,更易水平拓展。

Redis 数据持久化

在思否上找到了一篇非常好的文章《Redis持久化》介绍Redis两种持久化的方式。

•快照(RDB):在指定的时间间隔保存数据快照,如每天0点。优点:持久化文件简洁,便于数据恢复,启动速度更快。缺点:很难及时备份,数据丢失更多。

•追加式文件(AOF):默认每秒将内存中的数据刷到硬盘里,逐行追加。优点:相比RDB更可靠。缺点:性能会略有降低,AOF文件大小比RDB更大。

本文已收录于个人博客:tobetogether.xyz/news/articl…