Redis的数据类型
类型 | 简介 | 特性 | 场景 |
---|
String(字符串) | 二进制安全 | 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M | 可以用来做最简单的数据,可以缓存某个简单的字符串,也可以缓存某个json格式的字符串,Redis分布式锁的实现就利用了这种数据结构,还包括可以实现计数器、分布式ID | 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与分布式ID
分布式ID是什么?有哪些解决方案?
在开发中,我们通常会需要一个唯一ID来标识数据,如果是单体架构,我们可以通过数据库的主键,或直接在内存中维护一个自增数字来作为ID都是可以的,但对于一个分布式系统,就会有可能会出现ID冲突,此时有以下解决方案:
- uuid,这种方案复杂度最低,但是会影响存储空间和性能
- 利用单机数据库的自增主键,作为分布式ID的生成器,复杂度适中,ID长度较之uuid更短,但是受到单机数据库性能的限制,并发量大的时候,此方案也不是最优方案
- 利用redis、zookeeper的特性来生成id,比如
redis的自增命令 、zookeeper的顺序节点,这种方案和单机数据库(mysql)相比,性能有所提高,可以适当选用 - 雪花算法,一切问题如果能直接用算法解决,那就是最合适的,利用雪花算法也可以生成分布式ID,底层原理就是通过某台机器在某一毫秒内对某一个数字自增,这种方案也能保证分布式架构中的系统id唯一,但是只能保证趋势递增。业界存在tinyid、leaf等开源中间件实现了雪花算法。
Redis Incr 命令 将 key 中储存的数字值增一。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
Redis与分布式锁
目前主流的分布式锁的实现方案有两种:
- zookeeper:利用的是zookeeper的临时节点、顺序节点、watch机制来实现的,zookeeper分布式锁的特点是高一致性,因为zookeeper保证的是CP,所以由它实现的分布式锁更可靠,不会出现混乱
redis :利用redis的setnx 、lua 脚本、消费订阅机制 来实现的,redis分布式锁的特点是高可用,因为redis保证的是AP,所以由它实现的分布式锁可能不可靠,不稳定(一旦redis中的数据出现了不一致),可能会出现多个客户端同时加到锁的情况
Setnx命令:
Redis Setnx(SET if Not eXists) 命令 在指定的 key 不存在时,为 key 设置指定的值。返回值 设置成功,返回 1 。 设置失败,返回 0 。
Lua脚本:
通常把Lua Script 放到一个lua文件中,然后执行这个lua脚本。示例:活跃用户判断:判断一个游戏用户是否属于活跃用户,如果符合标准,则活跃用户人数+1
if redis.call("EXISTS",KEYS[1]) == 1 then
return 1
else
return 0
end
发布订阅模式:
- 开启本地 Redis 服务,开启两个 redis-cli 客户端。
- 在订阅者/消费者 redis-cli 客户端输入
SUBSCRIBE runoobChat ,意思是订阅 runoobChat 频道。 - 在发布者/生产者 redis-cli 客户端输入
PUBLISH runoobChat "Redis PUBLISH test" 往 runoobChat 频道发送消息,这个时候在第一个 redis-cli 客户端就会看到由第二个 redis-cli 客户端发送的测试消息。
Redis分布式锁底层是如何实现的?
- 首先利用setnx来保证:如果key不存在才能获取到锁,如果key存在,则获取不到锁
- 然后还要利用lua脚本来保证多个redis操作的原子性
- 同时还要考虑到锁过期,所以需要额外的一个看门狗定时任务来监听锁是否需要续约
- 同时还要考虑到redis节点挂掉后的情况,所以需要采用红锁的方式来同时向N/2+1个节点申请锁,都申请到了才证明获取锁成功,这样就算其中某个redis节点挂掉了,锁也不能被其他客户端获取到
整理 by zhengkai.blog.csdn.net
Redis主从复制的核心原理
全量同步:
- 一般发生在从节点初始化的时候
- 从节点发送SYNC命令连接主节点
- 主节点接收到
SYNC命令 后,开始执行BGSAVE命令 生成RDB 文件,并使用缓冲区 replication buffer 记录在这个过程中接收到的写命令 - 主节点BGSAVE命令执行完后,向所有从节点发送RDB文件,并在发送缓冲区 replication buffer 记录的写命令
- 从节点收到RDB文件后丢弃所有旧数据,载入收到的RDB文件中的数据
- 主节点RDB文件发送完毕后,开始向从节点发送缓冲区 replication buffer 中的写命令
- 从节点完成RDB的载入后,开始接收客户端命令,并执行来自主节点缓冲区 replication buffer 的写命令
增量同步: Redis主节点每执行一个写命令就会向从节点异步发送相同的写命令,从节点接收并执行收到的写命令
缓存穿透、缓存击穿、缓存雪崩分别是什么
缓存中存放的大多都是热点数据,目的就是防止请求可以直接从缓存中获取到数据,而不用访问Mysql。
总结:热点数据同时过期,过期时间设置随机值或高可用设计;缓存击穿是热点时效导致直连数据库,设置key不过期。不存在的值攻击造成缓存穿透,使用布隆过滤。
- 缓存雪崩:如果缓存中某一时刻大批热点数据同时过期,那么就可能导致大量请求直接访问Mysql了,解决办法就是在过期时间上增加一点随机值,另外如果搭建一个高可用的Redis集群也是防止缓存雪崩的有效手段
- 缓存击穿:和缓存雪崩类似,
缓存雪崩是大批热点数据失效 ,而缓存击穿是指某一个热点key突然失效,也导致了大量请求直接访问Mysql数据库 ,这就是缓存击穿,解决方案就是考虑这个热点key不设过期时间 - 缓存穿透:假如某一时刻访问redis的大量key都在redis中不存在(比如黑客故意伪造一些乱七八糟的key),那么也会给数据造成压力,这就是缓存穿透,解决方案是使用布隆过滤器,它的作用就是如果它认为一个key不存在,那么这个key就肯定不存在,所以可以在缓存之前加一层布隆过滤器来拦截不存在的key
Redis和Mysql如何保证数据一致
- 先更新Mysql,再更新Redis,如果更新Redis失败,可能仍然不一致
- 先删除Redis缓存数据,再更新Mysql,再次查询的时候在将数据添加到缓存中,这种方案能解决1方案的问题,但是在高并发下性能较低,而且仍然会出现数据不一致的问题,比如线程1删除了Redis缓存数据,正在更新Mysql,此时另外一个查询再查询,那么就会把Mysql中老数据又查到Redis中
延时双删 ,步骤是:先删除Redis缓存数据,再更新Mysql,延迟几百毫秒再删除Redis缓存数据 ,这样就算在更新Mysql时,有其他线程读了Mysql,把老数据读到了Redis中,那么也会被删除掉,从而把数据保持一致
Redis Stream 流式数据结构
Redis Stream 是 Redis 5.0 版本新增加的数据结构。
Redis Stream 主要用于消息队列(MQ,Message Queue),Redis 本身是有一个 Redis 发布订阅 (pub/sub) 来实现消息队列的功能,但它有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。
简单来说发布订阅 (pub/sub) 可以分发消息,但无法记录历史消息。
而 Redis Stream 提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。
Redis Stream 的结构如下所示,它有一个消息链表,将所有加入的消息都串起来,每个消息都有一个唯一的 ID 和对应的内容:
|