一、关于Redis
Redis介绍
REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统,是跨平台的非关系型数据库。
Redis 是一个开源的使用 ANSIC 语言编写、遵守 BSD(开源协议) 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API。
Redis 通常被称为数据结构服务器,因为值(value)可以是字符串(String)、哈希(Hash)、列表(list)、集合(sets)和有序集合(sorted sets)等类型。
Redis 优势
1、性能极高 -- Redis能读的速度是110000次/s,写的速度是81000次/s(官方数据,实际因测试设备网络不同会有不同程度的影响,但是效率还是极高) 。
2、丰富的数据类型 -- Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
3、原子 -- Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
4、丰富的特性 -- Redis还支持 publish/subscribe, 通知, key 过期等等特性。
5、基于内存操作。CPU不是redis的性能瓶颈
6、单线程运行可以省去很多上下文切换,不用考虑锁的问题
7、多路IO复用模型,使用了单线程来轮询描述符,减少了线程切换时上下文的切换和竞争,能带来更好的可维护性,方便开发和调试
Redis数据结构
String(字符串)
string 是 redis 最基本的类型,一个 key 对应一个 value
string 类型是二进制安全的。string 可以包含任何数据。比如jpg图片或者序列化的对象。
string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB
Hash(哈希)
hash 是一个键值(key=>value)对集合。
hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
List(列表)
列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
Set(集合)
Set 是 string 类型的无序集合,集合是通过哈希表实现的
zset(sorted set:有序集合)
zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
Redis各个数据类型应用场景
|------------------|------------------------------------|-----------------------------------------------------------------------------|-----------------------------------------------------------|
| 类型 | 简介 | 特性 | 场景 |
| String(字符串) | 二进制安全 | 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M | --- |
| Hash(字典) | 键值对集合,即编程语言中的Map类型 | 适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去) | 存储、读取、修改用户属性 |
| List(列表) | 链表(双向链表) | 增删快,提供了操作某一段元素的API | 1,最新消息排行等功能(比如朋友圈的时间线) 2,消息队列 |
| Set(集合) | 哈希表实现,元素不重复 | 1、添加、删除,查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作 | 1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐 |
| Sorted Set(有序集合) | 将Set中的元素增加一个权重参数score,元素按score有序排列 | 数据插入集合时,已经进行天然排序 | 1、排行榜 2、带权重的消息队列 |
注意:Redis支持多个数据库,并且每个数据库的数据是隔离的不能共享,并且基于单机才有,如果是集群就没有数据库的概念。每个数据库对外都是一个从0开始的递增数字命名,Redis默认支持16个数据库,支持切换数据库。
数据淘汰策略
- volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集中任意选择数据淘汰
- allkeys-lru:从所有数据集中挑选最近最少使用的数据淘汰
- allkeys-random:从所有数据集中任意选择数据进行淘汰
- noeviction:禁止驱逐数据 需要先设置最大内存maxmemory,然后如果内存不足,会触发我们选择的过期淘汰策略
持久化
分RDB 和AOF两种方式
RDB(Redis DataBase)
按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb;如果系统发生故障,将会丢失最后一次创建快照之后的数据。
需要注意的是:
RDB写入,每次都是全量,在数据量特别大时,服务器负载会比较高
RDB会在服务器宕机时,丢失几分钟的数据,主要是根据save策略来的
AOF(Append Only File)
AOF 以协议文本的方式,将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件,以此达到记录数据库状态的目的。(默认关闭,需要更改config配置文件来开启)
需要注意的是:
重写是直接把当前内存的数据生成对应命令,不需要分析老的AOF文件。
恢复数据时,会先判断有没有AOF,没有的话,在加载RDB,因为AOF文件相对完整。
Redis部署方式
|---------------|--------------------|-----------------------------|
| 模式 | 优点 | 缺点 |
| 单机版 | 架构简单,部署方便 | 机器故障、容量瓶颈、QPS瓶颈 |
| 主从复制 | 高可靠性,读写分离 | 故障恢复复杂,主库的写跟存受单机限制 |
| Sentinel 哨兵 | 集群部署简单,HA | 原理繁琐,slave存在资源浪费,不能解决读写分离问题 |
| Redis Cluster | 数据动态存储solt,可扩展,高可用 | 客户端动态感知后端变更,批量操作支持查 |
redis主从复制
该模式下 具有高可用性且读写分离, 会采用 增量同步 跟 全量同步 两种机制。
全量同步 :Redis全量复制一般发生在Slave 初始化阶段 ,这时Slave需要将Master上的所有数据都复制一份
增量同步:也叫指令同步。Redis会把指令存放在一个环形队列当中,因为内存容量有限,如果备机一直起不来,不可能把所有的内存都去存指令,也就是说,如果备机一直未同步,指令可能会被覆盖掉。增量复制的过程主要是master每执行一个写命令就会向slave发送相同的写命令。
哨兵模式
Redis-sentinel 本身是一个独立运行的进程,一般sentinel集群 节点数至少三个且奇数个,它能监控多个master-slave集群,sentinel节点发现master宕机后能进行自动切换。Sentinel可以监视任意多个主服务器以及主服务器属下的从服务器,并在被监视的主服务器下线时,自动执行故障转移操作。
哨兵用途:
集群监控:循环监控master跟slave节点。
消息通知:当它发现有redis实例有故障的话,就会发送消息给管理员
故障转移:这里分为主观下线(单独一个哨兵发现master故障了)。客观下线(多个哨兵进行抉择发现达到quorum数时候开始进行切换)。
配置中心:如果发生了故障转移,它会通知将master的新地址写在配置中心告诉客户端。
Redis Cluster
RedisCluster是Redis的分布式解决方案,在3.0版本后推出的方案,有效地解决了Redis分布式的需求。说白了就是把数据分布在不同的节点上,采用去中心化的思想
分区规则如下
二、Redis常见问题
雪崩、穿透、击穿
|-----------------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| 问题点 | 现象 | 解决方案 |
| ###### 缓存雪崩 | 缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩(由于原有缓存失效,新缓存未到期间) | 考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上;还有一个解决方案,原有的失效时间基础上增加一个随机值 |
| ###### 缓存穿透 | 查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义 | 1、如果一个查询返回的数据为空,我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟 2、使用布隆过滤器存储所有可能访问的 key,不存在的 key 直接被过滤,存在的 key 则再进一步查询缓存和数据库。 |
| ###### 缓存击穿 | 缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮 | 1、对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询 2、设置缓存不过期或者后台有线程一直给热点数据续期 |
布隆过滤器
布隆过滤器是防止缓存穿透的方案之一。布隆过滤器主要是解决大规模数据下不需要精确过滤的业务场景,如检查垃圾邮件地址,爬虫URL地址去重, 解决缓存穿透问题等。
布隆过滤器:在一个存在一定数量的集合中过滤一个对应的元素,判断该元素是否一定不在集合中或者可能在集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。
实现流程
布隆过滤器是基于bitmap和若干个hash算法实现的
1.元素tie经过hash1,hash2,hash3运算出对应的三个值落到了数组下标为4,6,8的位置上,并将其位置的默认值0,修改成1。
2.元素niu同理落到了数组下标为1,3,4的位置上,并将其位置的默认值0,修改成1。
此时bitmap中已经存储了tie,niu数据元素。
当请求想通过布隆过滤器判断tie元素在程序中是否存在时,通过hash运算结果到数组对应下标位置上发现值已经都被置为1,此时返回true。
总结布隆过滤器:如果布隆过滤器判断数据不存在,那么一定不存在,判断存在的不一定存在(有点绕。因为哈希冲突不可避免,所以过滤器判断存在的不一定存在)
Redis限流
在开发高并发系统时,有三把利器用来保护系统:缓存、降级和限流。目前主要有下面这几种限流的算法
1、基于Redis的setnx、zset
2、漏桶算法
3、令牌桶算法