Redis深度历险
2020-12-04 10:02:51 1 举报
AI智能生成
Redis深度应用解析
作者其他创作
大纲/内容
二、原理
1)线程IO模型
Redis是个单线程程序!
单线程的如何处理大量的并发客户端连接?
单线程的如何处理大量的并发客户端连接?
1、非阻塞IO
2、事件轮询(多路复用)
单个Redis节点在跑满一个CPU核心的情况下可以达到10W/S的超高QPS
2)持久化
1、快照RDB
Redis采用操作系统的多进程COW(copy on write)机制来实现快照持久化。
Redis在持久化时会调用glibc的函数fork产生一个子进程来处理快照持久化,
子进程上下文内存的数据在进程产生的一瞬间会凝固不会改变,这也是为何叫“快照”的原因。
优点:恢复快 缺点:遍历整个内存,大块写磁盘会加重系统负载;容易丢失大量数据
Redis在持久化时会调用glibc的函数fork产生一个子进程来处理快照持久化,
子进程上下文内存的数据在进程产生的一瞬间会凝固不会改变,这也是为何叫“快照”的原因。
优点:恢复快 缺点:遍历整个内存,大块写磁盘会加重系统负载;容易丢失大量数据
2、AOF日志
顺序记录指令序列,也就是“重放”。Redis提供bgrewriteaof指令对AOF日志进行重写而达到瘦身效果
优点:数据较完整 缺点:恢复慢;AOF的fsync是一个耗时的IO操作,会降低Redis性能,增加IO负担
优点:数据较完整 缺点:恢复慢;AOF的fsync是一个耗时的IO操作,会降低Redis性能,增加IO负担
3、RDB+AOF
Redis 4.0开始支持混合模式,在rdb持久化开始和结束的这段时间采用AOF记录增量日志。
恢复时候先加载rdb的内容,再重放增量AOF日志
恢复时候先加载rdb的内容,再重放增量AOF日志
3)管道 Pipeline
管道是Reids客户端提供的工具,通过对管道中的指令列表改变读写顺序就可以大幅节省IO时间,管道中指令越多效果越好
压力测试工具 redis-benchmark,对比正常指令和管道指令的效率差距:
> redis-benchmark -t set -q (qps 5w/s)
> redis-benchmark -t set -p 2 -q (qps 9w/s)
> redis-benchmark -t set -p 3 -q (qps 10w/s CPU到100%)
> redis-benchmark -t set -q (qps 5w/s)
> redis-benchmark -t set -p 2 -q (qps 9w/s)
> redis-benchmark -t set -p 3 -q (qps 10w/s CPU到100%)
4)事务
multi、exec、discard、watch 四个指令。multi指示事务开始,exec指示事务的执行,discard指示事务的丢弃,watch乐观锁
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
EXEC 命令负责触发并执行事务中的所有命令
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
EXEC 命令负责触发并执行事务中的所有命令
Redis 在事务失败时不进行回滚,而是继续执行余下的命令
三、集群
CAP理论
一致性、可用性、分区容忍性
当网络分区(分布式的特点)发生时,一致性和可用性两难全
集群策略
1、主从同步
增量同步
增量同步指令集。主节点把指令集缓存到本地内存Buffer,然后异步将buffer同步给从节点。
Ruffer为定长的环形数组,如果数组满了就会从头开始覆盖前面内容。将会引发快照同步
Ruffer为定长的环形数组,如果数组满了就会从头开始覆盖前面内容。将会引发快照同步
快照同步
主节点当前内存的数据全部快照到磁盘文件,传送到从节点,从节点执行全量加载(加载前清空当前内存)。
快照同步的过程中,会继续增量同步
快照同步的过程中,会继续增量同步
2、哨兵模式—Sentinel
哨兵模式一般采用3个以上Sentinel节点作为集群来监控redis主从状态。当主节点挂掉时,自动选择一个最优的从节点切换成为主节点。客户端来连接redis集群时会首先连接哨兵集群,通过哨兵集群来获取主节点地址,然后再连接主节点进行数据交互。当主节点发生故障时,客户端会重新向哨兵要替换地址,哨兵会将最新的主节点地址告诉客户端。
3、Codis代理
出身
中国人开源的,使用Go语言开发,前豌豆荚中间件团队。(后项目带头人刘奇又开发出TiDB)
Codis自身是无状态,只是转发客户端请求,可以集群。
分片原理
默认将所有key划分为1024个槽位。首先将客户端传过来的key进行crc32运算计算hash值,
再将hash后的整数值对1024这个整数进行取模得到一个余数,这个余数就是对应key的槽位。
再将hash后的整数值对1024这个整数进行取模得到一个余数,这个余数就是对应key的槽位。
槽位默认1024,支持设置,如2048,4096等
槽位信息存储
Codis需要个分布式配置存储数据库专门用来持久化槽位关系,目前支持zookeeper和etcd
分片扩容
Codis支持自动均衡功能,在系统空闲时观察每个Redis实例对应的slot数量,如果不平衡就自动就行迁移
优点
集群方案简单(增加一层代理)
缺点
不支持事务,事务只能在单个Redis实例中完成(所有分片方案的通病)
增加了Proxy中转层,网络开销比单个Redis大
集群配置中心使用zookeeper来实现,在部署上增加了运维代价
4、Cluster分片
出身
Redis的“亲儿子”,Redis作者自己提供的集群化方案
分片原理
Cluster将数据划分为16384个槽位,每个节点负责其中一部分槽位。
每个节点负责整个集群的一部分数据,节点间相互连接组成一个对等的集群,节点间通过一种特殊的二进制协议交互集群信息(Raft和Gossip)
默认对key值使用crc16算法进行hash,再将hash后的整数值对16384进行取模来得到具体槽位
槽位信息存储
槽位的位置信息存储于每个节点中,不像Codis需要额外的分布式存储
分片扩容
Cluster提供了工具redis-trib可以让运维人员手动调整槽位分配,使用Ruby语言开发,通过组合各种原生的cluster指令来实现。
redis-trib提供了UI界面来操作迁移,同事提供自动化平衡槽位工具,无需人工干预就可以均衡集群负载
redis-trib提供了UI界面来操作迁移,同事提供自动化平衡槽位工具,无需人工干预就可以均衡集群负载
数据迁移过程中源节点主线程会处于阻塞状态,直到key被删除。源节点获取key内容->存到目标节点->源节点删除key
容错性
Cluster的每个分片节点都可以是主从同步结构,当主节点故障,从节点提升为主节点
由于Cluster是去中心化的,只有大多数节点都认定某个节点失联了,集群才认为该节点需要进行主从切换来容错
一、基础和应用
1)概念
Remote Dictionary Service(远程字典服务);Redis以其超高的性能、
完美的文档、简洁易懂的源码和丰富的客户端库支持在开源中间件领域广受好评。
完美的文档、简洁易懂的源码和丰富的客户端库支持在开源中间件领域广受好评。
2)基础数据结构
5种基础数据结构
string-字符串
list-列表
相当于java的LinkedList,是链表而不是数组
hash-字典
相当于java的HashMap
set-集合
无序、唯一;相当于java的HashSet
zset-有序集合
相当于java的SortedSet和HashMap结合体。一方面value有序、唯一,
另一方面可以给每个value一个score,代表这个value的排序权重,
内部实现采用“跳跃列表”的数据结构
另一方面可以给每个value一个score,代表这个value的排序权重,
内部实现采用“跳跃列表”的数据结构
通用原则
list、set、hash、zset这四种数据结构是容器型数据结构,共享两条通用原则
1. create if not exists: 如果容器不存在,那就创建一个再操作
2. drop if no elements: 如果容器里没有元素了,立即删除容器释放内存
3)高级数据结构
位图
Redis中的位图没有特殊的数据结构,可以理解为基本的Redis结构字符串的位级操作
命令:setbit/getbit
业务场景:如网站签到记录
HyperLogLog
提供不精确的去重计数方案,标准误差0.81%;但是相比set能大量节省存储空间;
在计数比较小时,存储空间采用稀疏矩阵存储,空间占用很小。仅仅在计数慢慢变大,稀疏矩阵
占用空间渐渐超过阈值时,才会一次性转变成稠密矩阵,空间占用固定为12KB。
在计数比较小时,存储空间采用稀疏矩阵存储,空间占用很小。仅仅在计数慢慢变大,稀疏矩阵
占用空间渐渐超过阈值时,才会一次性转变成稠密矩阵,空间占用固定为12KB。
命令:pfadd/pfcount
业务场景:记录网站UV(PV可以使用incrby计数)
布隆过滤器
一个不怎么精确的set结构,在起到去重作用的同时,空间占用较小,提供contains方法判断某个对象是否存在,但是可能会误判;
当布隆过滤器说某个值存在时,这个值可能不存在,当说某个值不存在时,那就肯定不存在。
Redis 4.0提供了插件功能后,布隆作为一个插件加载到Redis Server里
当布隆过滤器说某个值存在时,这个值可能不存在,当说某个值不存在时,那就肯定不存在。
Redis 4.0提供了插件功能后,布隆作为一个插件加载到Redis Server里
命令:bf.add/bf.exists
业务场景:推荐系统的内容去重,能准确过滤掉用户已经看过的内容,识别绝大多数用户没看过的新内容,保证推荐给用户的内容都是无重复的
4)分布式锁
解决并发场景下非原子操作问题
通过设置key值占坑,用完了使用del指令释放
setnx(set if not exists) + expire
组合指令:set xxkey true ex 5 nx,解决setnx和expire两个指令的非原子性
超时问题
Redis分布式锁不要用于执行时间太长的任务,否则超出锁的过期时间会导致锁提前释放
5)延时队列
使用场景
只有一组消费者的消息队列
不需要ACK保证,对消息可靠性没有极高要求
6)限流
简单限流
采用zset数据结构,score存储滑动时间窗口(定宽),value值没有实际意义,保证为唯一即可
使用场景:防抖动;
每一个用户行为到来时,都维护一次时间窗口,将时间窗口外的记录全部清理掉,只保留窗口内的记录
每一个用户行为到来时,都维护一次时间窗口,将时间窗口外的记录全部清理掉,只保留窗口内的记录
漏斗限流
Redis 4.0提供了一个限流Redis模块:Redis-Cell,该模块使用了漏斗算法,并提供了原子的限流命令cl.throttle
Redis-Cell作者Itamar Haber,使用Rust语言编写;Redis使用C语言编写,但是Redis的模块可以使用其他语言实现
7)地理位置
Redis在3.2版本之后增加了地理位置Geo模块,意味着我们可以使用Redis来实现类似摩拜单车的附近的车,美团和饿了么的附近的餐馆等功能
业界比较通用的地理位置距离排序算法是GeoHash算法,Redis也使用的GeoHash;算法逻辑将二维的经纬度数据映射成一维的整数
Geo的存储结构使用的zset
8)大海捞针—scan
从Redis实例的成千上万个key中找出特定前缀的key列表
scan vs keys
keys
keys带正则表达式,没有offset、limit参数,一次性吐出所有满足条件的key
keys是遍历算法,复杂度为O(n),由于redis是单线程顺序执行所有指令,会延后其他指令执行
scan
scan指令虽然复杂度也是O(n),但它是通过游标分布进行的,不会阻塞线程
提供limit参数,控制每次返回的最大条数
单次返回的结果是空的并不意味着遍历结束,要看返回的游标值是否为0
命令:scan [游标值] match [key正则] count [数量]
0 条评论
下一页