什么是Redis
简单来说
Redis 就是一个使用 C 语言开发的数据库
,不过与传统数据库不同的是
Redis 的数据是存在内存中的
,也就是它是内存数据库,所以读写速度非常快,因此 Redis 被广泛应用于缓存方向。
另外,
Redis 除了做缓存之外,Redis 也经常用来做分布式锁,甚至是消息队列。
Redis 提供了多种数据类型来支持不同的业务场景。Redis 还支持事务 、持久化、Lua 脚本、多种集群方案。
Redis支持的数据类型及其应用场景
1.string
介绍
:String类型是Redis最基本的数据类型,String数据结构是简单的 key-value 类型,Redis的String可以包含任何数据。比如jpg图片或者序列化的对象 。
命令
:
- set/get:存放(取出)单个key-value
- mset/mget:存放(取出)多个key-value
- type key:查看key的类型
- exists key:判断指定key是否存在
- keys *: 查看有哪些key
- del key:删除key
- TTL key: key的有效时间,-1永久生效,-2删除
- expire key time:设置有效时长
- pexpire key time: 设置毫秒
应用场景
:
- 缓存功能:字符串最经典的使用场景,Redis作为缓存层,MySQL作为储存层,绝大部分请求数据都是Redis中获取,由于Redis具有支撑高并发特性,所以缓存通常能起到加速读写和降低后端压力的作用。
- 计数器:许多运用都会使用Redis作为计数的基础工具,他可以实现快速计数、查询缓存的功能,同时数据可以一步落地到其他的数据源。
例如:视频播放数系统就是使用Redis作为视频播放数计数的基础组件。- 共享Session:出于负载均衡的考虑,分布式服务会将用户信息的访问均衡到不同服务器上,用户刷新一次访问可能会需要重新登录,为避免这个问题可以用Redis将用户Session集中管理,在这种模式下只要保证Redis的高可用和扩展性的,每次获取用户更新或查询登录信息都直接从Redis中集中获取。
- 限速:处于安全考虑,每次进行登录时让用户输入手机验证码,为了短信接口不被频繁访问,会限制用户每分钟获取验证码的频率。
2.list
介绍
:
list
即是
链表
。Redis 的 list 的实现为一个
双向链表
,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
命令
:
- lpush/lpop name value:从左存放(取出)单个key-value
- rpush/rpop name value:从右存放(取出)单个key-value
- lrange lname 0 -1:从左向右遍历整个列表
应用场景
:
- 消息队列: Redis的lpush+rpop命令组合即可实现阻塞队列,生产者客户端是用lpush从列表左侧插入元素,多个消费者客户端使用rpop命令阻塞时的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性
3.hash
介绍
:hash 是一个 string 类型的 field 和 value 的映射表,
特别适合用于存储对象
,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等
命令
:
- hset/hget
- hmset/hmget
- hdel field:删除field
应用场景
:
- 系统中对象数据的存储。
4.set
介绍
:Redis 中的 set 类型是一种无序集合,集合中的元素没有先后顺序。当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)
命令
:
- SADD key member1 [member2]:向集合添加一个或多个成员
- SCARD key:获取集合的成员数
- SISMEMBER key member:判断 member 元素是否是集合 key 的成员
- SMEMBERS key:返回集合中的所有成员
- SPOP key:移除并返回集合中的一个随机元素
- SREM key member1 [member2]:移除集合中一个或多个成员
应用场景
:
- 需要存放的数据不能重复
- 需要获取多个数据源交集和并集等场景
5.sorted set(zset)
介绍
:Sorted Set和Set一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。
命令
:
- ZADD zset1 80 a 90 b 60 c
- ZRANGE zset1 0 -1:遍历整个zset
- ZREM:删除元素
应用场景
:
- 系统中对象数据的存储。
Redis的持久化机制及其策略
1、RDB持久化
介绍
:
1、是Redis默认的持久化方式。
2、Redis 可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。
3、Redis 创建快照之后,可以对快照进行备份
4、可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis 主从结构,主要用来提高 Redis 性能),
5、还可以将快照留在原地以便重启服务器的时候使用,以.rdb结尾的文件
6、可能存在数据丢失,比如正要保存某个时期的快照时,服务器突然宕机,此时数据就没有保存上
持久化策略
save 900 1
#在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 300 10
#在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 60 10000
#在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
2、AOF持久化
介绍
:
1、与快照持久化相比,AOF (append-only file)持久化 的实时性更好。
2、默认情况下 Redis 没有开启 AOF持久化,可以通过 appendonly 参数开启
3、开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入硬盘中的 AOF 文件。
4、AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是 appendonly.aof。
5、开启aof持久化后,服务器重启是会优先读取aof文件
持久化策略
appendfsync always
#每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec
#每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no
#让操作系统决定何时进行同步
为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步一次 AOF 文件,Redis 性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis 还会优雅的放慢自己的速度以便适应硬盘的最大写入速度
Redis的集群模式
1、主从复制
1、是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower) ;
2、数据的复制是单向的,只能由主节点到从节点。Master以写为主,Slave以读为主。
3、默认情况下,每台Redis服务器都是主节点 ;
4、且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
主从复制的作用
读写分离:主节点写,从节点读,提高服务器的读写负载能力
数据冗余︰主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
故障恢复︰当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复 ; 实际上是一种服务的冗余。
负载均衡︰在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载; 尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
高可用(集群)基石︰除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
2、哨兵模式
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
用文字描述一下
故障切换
(failover)的过程。假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为
主观下线
。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为
客观下线
。这样对于客户端而言,一切都是透明的。
3、Redis Cluster
Redis Cluster特点
- 多主多从,去中心化:从节点作为备用,复制主节点,不做读写操作,不提供服务
- 不支持处理多个key:因为数据分散在多个节点,在数据量大高并发的情况下会影响性能;
- 支持动态扩容节点
- 节点之间相互通信,相互选举,不再依赖sentinel:准确来说是主节点之间相互“监督”,保证及时故障转移
Redis Cluster是如何存储数据的?
Redis集群使用一种称作一致性哈希的复合分区形式(组合了哈希分区和列表分袂的特征来计算键的归属实例),键的CRC16哈希值被称为哈希槽。比如对于三个Redis节点,哈希槽的分配方式如下:
- 第一个节点拥有0-5500哈希槽
- 第二节点拥有5501-11000哈希槽
- 第三节点拥有剩余的11001-16384哈希槽
一个键的对应的哈希槽通过计算键的CRC16 哈希值,然后对16384进行取模得到,即
HASH_SLOT=CRC16(key) modulo 16383
,Redis提供了
CLUSTER KEYSLOT
命令来执行哈希槽的计算:
Redis的缓存问题
1、缓存击穿
介绍
:在平常高并发的系统中,大量的请求同时查询一个 key 时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去
解决
:
- 热点数据不设置过期时间
- 上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它。其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存。
2、缓存穿透
介绍
:正常情况下,我们去查询数据都是存在。那么请求去查询一条压根儿数据库中根本就不存在的数据,也就是缓存和数据库都查询不到这条数据,但是请求每次都会打到数据库上面去
解决
:
- 存空值:之所以会发生穿透,就是因为缓存中没有存储这些空数据的key。从而导致每次查询都到数据库去了。那么我们就可以为这些key对应的值设置为null 丢到缓存里面去。后面再出现查询这个key 的请求的时候,直接返回null 。这样,就不用在到数据库中去走一圈了,但是别忘了设置过期时间
- 布隆过滤器:把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤器中,不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程
3、缓存雪崩
介绍
:当某一时刻发生大规模的缓存失效的情况,比如你的缓存服务宕机了,会有大量的请求进来直接打到数据库上面。结果就是数据库撑不住,挂掉了
解决
:
- 使用集群缓存,保证缓存服务的高可用这种方案就是在发生雪崩前对缓存集群实现高可用,如果是使用 Redis,可以使用 主从+哨兵 ,Redis Cluster 来避免 Redis 全盘崩溃的情况
- 限制请求流量
- 缓存永不失效
Redis的应用场景
(1)会话缓存(Session Cache)
最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在他们还会这样吗?
幸运的是,随着Redis这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。
(2)全页缓存(FPC)
除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。
(3)队列
Reids在内存存储引擎领域的一大优点是提供list和set操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对list的push/pop操作。
(4)排行榜/计数器
Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted
Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。
(5)发布/订阅
最后是 Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用 Redis的发布/订阅功能来建立聊天系统。
附:参考链接
1、Redis支持哪几种数据类型?
2、常见三大缓存问题分析及解决方案
3、Redis——Redis主从复制(工作流程详解)
4、认识Redis集群——Redis Cluster