Redis 探索Ⅰ

本文主要讨论了Redis数据处理、Redis单线程工作模式与Redis的5大Value类型的使用场景相关话题。

一、Redis 数据处理

1.1 二进制安全

redis是二进制安全的,只存储客户端传输过来的二进制数据。数据是什么编码redis并不关注,序列化与反序列化交由客户端完成。

1.2 处理数据的流程

  1. 客户端有数据到来
  2. epoll 通知工作线程(worker)有数据到来
  3. 工作线程(worker)启动 I/O read 操作去读取到来的网络数据
  4. 对网络数据进行一系列计算
  5. 工作线程(worker)启动 I/O write 操作,将数据写入网络缓冲区
  6. 将数据由 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

  1. 作字符串存储
    进行对字符串的追加等操作。
    可用于缓存数据等。
  2. 作数值操作
    当执行一次 INCR 或 DEVCR 成功后,该数据的内部数据类型(type)会设定为 int,下一次执行 INCR 与 DECR 时将直接进行相关操作,不进行类型判定。而 INCR 和 DECR 为原子操作(单线程)。
    可用于秒杀、限流等。
  3. 存BITMAP
    对bit位进行按位与或非、统计1的个数等操作。
    (1) 可用于统计用户任意时间窗口内登录几次。即用户第几天登录了就在偏移几的地方设置1,当统计用户全年登录几次时,使用 BITCOUNT 统计1的个数即可。
    (2) 可用于统计月活。统计用户每日状态,然后对所有统计的用户进行按位或(OR)操作,再BITCOUNT其中1的个数,即是月活量。

3.2 list

  1. 队列
  2. 数组

3.3 hash

  1. 可用于做订单详情页,将订单编号做 key 值存储。
  2. 可用于聚合数据:用户的不同数据可能放在不同的库中,当用户要获取这些数据时,可以将不同库中的数据聚合到 redis 中,除第一次访问外,下次用户访问的时候,直接返回聚合的数据。

3.4 set

  1. 去重
    可用作推荐系统等。
  2. 无序
    可用作实现随机事件,例如抽奖、验证码等。

3.5 hset

hset是有序集合,其底层数据结构实现是跳跃表(skiplist)。其排序是根据score值的大小排序的,默认是升序排列。

  1. 去重
    可用作推荐系统等。
  2. 有序
    可用作排行榜与动态翻页的实现。

四、应用场景总结

缓存、统计bitmap、数值计算(秒杀、限流)、数据迁出(无状态)、聚合数据(详情页)、随机事件(抽奖)、集合(推荐系统)、有序集合(排行榜、翻页)、锁、发布订阅…

4.1 场景举例

  1. 若此时很多来源不一的并发客户端会对商品A进行CRUD操作,此时如何保证客户端操作的一致性?
    将不同的商品路由到不同的服务器(同一服务器只处理同一商品),此时服务A在调用redis的时候,A的所有操作都会被压在一个connection中,使得对其的CRUD操作都是有序的。
    若商品是随机路由到服务器上,此时为了保证对 redis 中数据操作的有序性,将牵引出一系列的事务性操作。