1 Redis 简介
Redis是一个 基于内存亦可持久化的Key-Value数据库,
Redis为什么这么快?
- redis是基于内存的,内存的读写速度非常快
- 采用单线程(网络请求模块使用了一个线程),避免了不必要的上下文切换和竞争条件
- Reactor的事件驱动模型,非阻塞IO - IO多路复用1
基本的数据结构,
- String: 字符串
- Hash: 散列
- List: 列表
- Set: 集合
- Sorted Set: 有序集合
2 Redis 常见使用场景
Hash
hash 是一个 string 类型的 field 和 value 的映射表 ,适合用于存储对象
比如说登录成功后存一些用户的token 以及个人信息
key=User293847
value={
"id": 1,
"name": "SnailClimb",
"age": 22,
"location": “Wuhan, Hubei”
"token": “PaZzZtT15ijfHcxqPaZzZtT15ijfHcxq”
}
Sorted Set
实时排行榜功能
# 添加分数
zadd top_buy 89 user1
zadd top_buy 95 user2
zadd top_buy 95 user3
zadd top_buy 90 user4
# 查询分数
zrevrange top_buy 0 2 withscores
List
基于List的 LPUSH+BRPOP 的实现的简单 入门级 消息列队
lpush和rpop左进右出
rpush和lpop右进左出
blpush和brpop堵塞式,可以指定超时时间
//发送消息
$redis->lPush($list, $value);
//消费消息
while (true) {
try {
$msg = $redis->rPop($list);
if (!$msg) {
sleep(1);
}
//业务处理
} catch (Exception $e) {
echo $e->getMessage();
}
}
不阻塞 lpop和rpop会一直空轮训,消耗资源 ,阻塞 空闲连接的会主动断开连接,减少闲置资源占用,就会抛出异常 所以还要捕获到异常,和重试,
Psubscribe
基于psubscribe 实现简单的 订单超时 ,关闭订单功能
1 修改redis.conf 开启keyspace notifications
SETEX order_info 60 redis
psubscribe 'keyevent@15:expired'
**Stream **
基于Stream 实现消息队列
// ,消息ID 方案,消息内容,其中消息内容为 key-value 型数据。ID,最常使用*,表示由 Redis 生成消息ID,这也是强烈建议的方案。field string [field string], 就是当前消息内容,由1个或多个 key-value 构成
XADD key ID field string [field string ...]
//从 Stream 中读取消息
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
//消息 ID 的序列化生成
//消息遍历
//消息的阻塞和非阻塞读取
//消息的分组消费
//未完成消息的处理
//消息队列监控
单点的 redis分布式锁(非严格意义上的分布式锁)
1、加锁
加锁实际上就是在redis中,给Key键设置一个值,为避免死锁,并给定一个过期时间。
SET lock_key random_value NX PX 2000
值得注意的是:random_value
是客户端生成的唯一的随机字符串。NX
代表只在键不存在时,才对键进行设置操作。PX 2000
设置键的过期时间为2000毫秒。
2 释放锁
// random_value 为:ARGV[1],而 lock_key 为KEYS[1]。
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
为什么不用 SETNX获取锁 主要原因是SETNX不支持超时时间的设置。
为什么random_value 要设置成随机值? 保证了一个客户端释放的锁是自己持有的那个锁
3 Redis 常见一些问题
1 Redis 持久化机制
RDB
fork一个进程,遍历hash table,利用copy on write,把整个db dump保存下来,粒度比较大,如果save, shutdown, slave 之前crash了,则中间的操作没办法恢复。
AOF
把写操作指令,持续的写到一个类似日志文件里,粒度较小,crash之后,只有crash之前没有来得及做日志的操作没办法恢复。
2 Redis 缓存穿透/缓存雪崩如何处理
缓存雪崩
- 失效时间分散开
- 增加互斥锁,控制数据库请求 防止集体落在db上
- 永远不过期 过期时间放value ,发现过期 异步线程重新构建换成
- 本地ehcache缓存+熔断设计
缓存雪崩
- 缓存空值 ,
- 布隆过滤器 (类似于一个hbase set 用来判断某个元素(key)是否存在于某个集合中
我们把有数据的key都放到BloomFilter中,每次查询的时候都先去BloomFilter判断,如果没有就直接返回null)
3 如何保证缓存与数据库 数据一致性?
读的顺序是先读缓存,后读数据库
写的顺序是先写数据库,然后写缓存
每次更新了相关的数据,都要把该缓存清理掉
为了避免极端条件下造成的缓存与数据库之间的数据不一致,缓存需要设置一个失效时间。时间到了,缓存自动被清理,达到缓存和数据库数据的“最终一致性”
Redis 高可用
Redis Sentinel 哨兵 一主二从三哨兵
哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
Redis Cluster 三 主 三 从
用虚拟槽分区,所有的键根据哈希函数映射到 0~16383 个整数槽内,每个节点负责维护一部分槽以及槽所映射的键值数据。
redis中的异步复制情况下的数据丢失
以及脑裂
问题
# 第一个参数表示连接到master的最少slave数量
# 第二个参数表示slave连接到master的最大延迟时间
min-replicas-to-write 3
min-replicas-max-lag 10
4 Redis 知识点
- Redis 之 Reactor ,I/O多路复用
- 缓存淘汰算法之LRU
- redis中哈希一致性
- 多路 I/O 复用模型是利用select、poll、epoll可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中唤醒,复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求 ↩︎