本文主要讨论了Redis数据处理、Redis单线程工作模式与Redis的5大Value类型的使用场景相关话题。
一、Redis 数据处理
1.1 二进制安全
redis是二进制安全的,只存储客户端传输过来的二进制数据。数据是什么编码redis并不关注,序列化与反序列化交由客户端完成。
1.2 处理数据的流程
- 客户端有数据到来
- epoll 通知工作线程(worker)有数据到来
- 工作线程(worker)启动 I/O read 操作去读取到来的网络数据
- 对网络数据进行一系列计算
- 工作线程(worker)启动 I/O write 操作,将数据写入网络缓冲区
- 将数据由 TCP 发送回客户端
二、Reids 单线程工作模式
2.1 数据库软件串行化设计的好处
- 避免了并发情况下的事务性操作(加锁)
- 例如:并发来两个对同一个数据的 ++ 操作。若非串行化设计,则要开启事务性,从而实现对数据的原子性操作;而在串行设计中,只需要依次操作即可,省去了加锁过程,提升了处理速度。
2.2 Redis早期的单线程设计
- 数据处理:I/O读->串行化计算->I/O写
- 缺陷:如果有多个数据到来,只能等待上一个数据处理完成。单线程只能工作在CPU的一个核心上,对如今多核CPU有资源的浪费。
2.3 Redis改进的单线程设计
- 利用多个线程(I/O threads)去执行I/O的读取和发送操作,计算的工作依旧由单线程去完成,以次保证串行化的原子操作,以此来缩短阻塞等待的时间。
- 注:I/O 的多线程操作并不会影响一个连接(connection)的有序性。
2.4 总结
- 无论那个版本的redis都只有一个工作线程,始终保持对数据的原子操作。而之后的版本使用了I/O多线程,减少了数据等待时间。
三、Redis 的5大Value类型场景
3.1 string
- 作字符串存储
进行对字符串的追加等操作。
可用于缓存数据等。 - 作数值操作
当执行一次 INCR 或 DEVCR 成功后,该数据的内部数据类型(type)会设定为 int,下一次执行 INCR 与 DECR 时将直接进行相关操作,不进行类型判定。而 INCR 和 DECR 为原子操作(单线程)。
可用于秒杀、限流等。 - 存BITMAP
对bit位进行按位与或非、统计1的个数等操作。
(1) 可用于统计用户任意时间窗口内登录几次。即用户第几天登录了就在偏移几的地方设置1,当统计用户全年登录几次时,使用 BITCOUNT 统计1的个数即可。
(2) 可用于统计月活。统计用户每日状态,然后对所有统计的用户进行按位或(OR)操作,再BITCOUNT其中1的个数,即是月活量。
3.2 list
- 栈
- 队列
- 数组
3.3 hash
- 可用于做订单详情页,将订单编号做 key 值存储。
- 可用于聚合数据:用户的不同数据可能放在不同的库中,当用户要获取这些数据时,可以将不同库中的数据聚合到 redis 中,除第一次访问外,下次用户访问的时候,直接返回聚合的数据。
3.4 set
- 去重
可用作推荐系统等。 - 无序
可用作实现随机事件,例如抽奖、验证码等。
3.5 hset
hset是有序集合,其底层数据结构实现是跳跃表(skiplist)。其排序是根据score值的大小排序的,默认是升序排列。
- 去重
可用作推荐系统等。 - 有序
可用作排行榜与动态翻页的实现。
四、应用场景总结
缓存、统计bitmap、数值计算(秒杀、限流)、数据迁出(无状态)、聚合数据(详情页)、随机事件(抽奖)、集合(推荐系统)、有序集合(排行榜、翻页)、锁、发布订阅…
4.1 场景举例
- 若此时很多来源不一的并发客户端会对商品A进行CRUD操作,此时如何保证客户端操作的一致性?
将不同的商品路由到不同的服务器(同一服务器只处理同一商品),此时服务A在调用redis的时候,A的所有操作都会被压在一个connection中,使得对其的CRUD操作都是有序的。
若商品是随机路由到服务器上,此时为了保证对 redis 中数据操作的有序性,将牵引出一系列的事务性操作。