Dawn's Blogs

分享技术 记录成长

0%

本节介绍一些在 Redis 中除了五大基本数据类型之外的数据类型,包括用法和使用场景。

BitMap

BitMap 位图,用于存储一串连续的二进制数组,可以通过 offset 定位元素,特别适合一些数据量大且使用二值统计的场景。

位图之间可以做逻辑运算,如与、或、非、异或。

内部实现

BitMap 底层使用 string 类型作为实现,Redis 的字符串类型是二进制安全的,所以可以作为位图的底层数据结构。

HyperLogLog

Redis HyperLogLog 是 Redis 2.8.9 版本新增的数据类型,用于统计一个集合中不重复的元素个数(不精确的去重计数)。但要注意,HyperLogLog 是统计规则是基于概率完成的,不是非常准确,**标准误算率是 0.81%**。

HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的内存空间总是固定的、并且是很小的。在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数,和元素越多就越耗费内存的 Set 和 Hash 类型相比,HyperLogLog 就非常节省空间。

GEO

Redis GEO 是 Redis 3.2 版本新增的数据类型,主要用于存储地理位置信息,并对存储的信息进行操作。

内部实现

GEO 本身并没有设计新的底层数据结构,而是直接使用了 Sorted Set 集合类型

GEO 类型使用 GeoHash 编码方法实现了经纬度到 Sorted Set 中元素权重分数的转换。这样一来,就可以把经纬度保存到 Sorted Set 中,利用 Sorted Set 提供的按权重进行有序范围查找的特性,实现 LBS 服务中频繁使用的搜索附近的需求。

Stream

Redis Stream 是 Redis 5.0 版本新增加的数据类型,Redis 专门为消息队列设计的数据类型。有以下两个特性:

  • 自动生成全局唯一消息 ID
  • 支持以消费组形式消费数据。

过期删除

Redis 可以设置 key 的过期时间,对于过期的 key,Redis 需要进行清理。

过期时间

当对一个 key 设置了过期时间,Redis 会把该 key 带上过期时间存储到一个过期字典(哈希表)中,过期字典保存了所有 key 的过期时间。所以在访问一个 key 时,可以在常数级时间复杂度查找 key 是否过期:

  • 不在过期字典中,说明没有设置过期时间。
  • 在过期字典中,获取过期时间,与系统当前时间进行比对是否过期。

删除策略

Redis 采用惰性删除+定期删除两种删除策略配合使用,以求在合理使用 CPU 时间和避免内存浪费之间取得平衡。

惰性删除

惰性删除中,不主动删除过期 key,只有当查询到过期 key 时才进行删除操作,删除可以选择异步删除或者同步删除。

惰性删除节省 CPU 时间,但是浪费内存

定期删除

定期删除策略的做法是,每隔一段时间随机从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。

定期删除相对于惰性删除更需要 CPU 资源,但是更加节约内存

Redis 实现的定期删除需要解决两个问题:

  1. 定期检查间隔时间:默认每 10 秒钟检查一次,这是可以配置的。
  2. 随机抽查的数量:数量是写死在代码中的,数值是 20

Redis 的定期删除的流程

  1. 从过期字典中随机抽取 20 个 key;
  2. 检查这 20 个 key 是否过期,并删除已过期的 key;
  3. 如果本轮检查的已过期 key 的数量,比例大于 25%(5 个),则继续重复步骤 1;否则停止继续删除过期 key,然后等待下一轮再检查。

Redis 为了保证定期删除不会出现循环过度,导致线程卡死现象,为此增加了定期删除循环流程的时间上限,默认不会超过 25ms。

阅读全文 »

Redis 单线程模型

Redis 单线程指的是「接收客户端请求->解析请求 ->进行数据读写等操作->发送数据给客户端」这个过程是由一个线程(主线程)来完成的,这也是我们常说 Redis 是单线程的原因。

后台线程

但是,Redis 程序并不是单线程的,Redis 在启动的时候,是会启动后台线程(BIO)的:

  • Redis 在 2.6 版本,会启动 2 个后台线程,分别处理关闭文件AOF 刷盘这两个任务;
  • Redis 在 4.0 版本之后,新增了一个新的后台线程,用来异步释放 Redis 内存,也就是 lazyfree 线程。例如执行 unlink key / flushdb async / flushall async 等命令,会把这些删除操作交给后台线程来执行,好处是不会导致 Redis 主线程卡顿。

因此,当删除一个大 key 的时候,不要使用 del 命令删除,因为 del 是在主线程处理的,这样会导致 Redis 主线程卡顿,因此我们应该使用 unlink 命令来异步删除大key。

阅读全文 »

Buffer Pool 介绍

MySQL 的数据是存储在磁盘中的,但是直接读取磁盘数据性能差。所以为了提升查询性能,加入缓冲池 buffer pool。有了 buffer pool 之后:

  • 读取数据时,如果数据存在于 Buffer Pool 中,客户端就会直接读取 Buffer Pool 中的数据,否则再去磁盘中读取。
  • 修改数据时,首先是修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页,最后由后台线程将脏页写入到磁盘。

buffer pool 大小

在 MySQL 启动时,会申请一段连续的存储空间作为 buffer pool,默认配置下 buffer pool 有 128 MB。

可以修改 innodb_buffer_pool_size 参数来设置 buffer pool 的大小,一般建议设置成**可用物理内存的 60%~80%**。

缓存内容

InnoDB 在存储数据时,会划分若干个,以页作为磁盘和内存交互的基本单位。所以,buffer pool 也是按照页划分的,一个页的默认大小为 16 KB。

在 MySQL 启动的时候,InnoDB 会为 Buffer Pool 申请一片连续的内存空间,然后按照默认的 16KB 的大小划分出一个个的页, Buffer Pool 中的页就叫做缓存页。MySQL 刚启动时,使用的虚拟内存空间很大,但是物理内存空间很小,只有这些虚拟内存被访问后,操作系统才会触发缺页中断,接着将虚拟地址和物理地址建立映射关系。

Buffer pool 除了缓存数据页索引页,还包括了回滚页、插入换成、自适应哈希索引、锁信息等。

img

阅读全文 »

模型部署按照部署设备分类,主要分为两类:

  • 云端/服务器上的部署,将模型作为一个线上服务进行部署。
  • 移动端/设备端上的部署,就是将模型作为 SDK 进行部署。

本文主要探讨模型作为一个线上服务进行部署。

作为线上服务部署

把 AI 模型作为一个线上服务部署,就需要提供外界访问的接口:

  • Restful HTTP 接口,使用 Flask、Django 等构建 HTTP API 服务。
  • RPC 接口,如 GRPC、Thrift 等。

也可以使用深度学习框架 Serving 实现快速部署:Torch Serving、Tensorflow Serving、Triton 等。

服务优化

模型部署涉及到了服务优化,例如内存占用太大怎么办、响应速度太慢怎么办,如何在线模型更新等等问题。

更小的内存占用

优化内存占用,有两个基本思路:

  • 模型的参数都需要保存在内存中?
  • 把模型变小。

分布式 kv 存储

如下图的模型结构,分为两个部分:

  • dense 网络:右侧深层网络,输入用户画像、用户行为序列等高级特征,结构较复杂。
  • sparse 网络:左侧浅层网络,输入用户或物料 id,结构非常简单。

推荐模型中,由于用于、候选物料极多, sparse 网络的参数占模型参数的 99%。但是进行预测时,sparse 网络只有输入的用户和候选内容 ID 对应的节点参与运算,dense 网络大部分节点都参与运算

image-20231030113409693

dense 网络每次计算都用到,且占用空间小,不适合独立存储;sparse 网络每次计算只用到很少一部分参数,适合独立存储。

所以将 dense 网络存储在内存中,sparse 网络以分布式 kv 的方式进行存储

压缩模型

多用于 NLP、CV 等领域,可以使用:

  • 模型量化(把模型从浮点数变为整数 INT8)。
  • 蒸馏(将复杂的网络特征提取出来,迁移到参数量小的网络中)。

压缩模型会使得模型的效果变差,要在模型效果和计算速度上进行 balance。

更快的推理速度

模型加速推理的优化方向:

  • 算法层加速:
    • 训练前:选用轻量级的网络。
    • 训练后加速
  • 框架层加速:tensorRT 等。
  • 硬件层加速:使用 GPU 而不是 CPU。

算法层加速 - 训练后加速

训练后加速方法:

  • 模型量化。
  • 模型蒸馏。
  • 模型剪枝:去掉模型中不重要的参数。

模型剪枝可行性:研究表明,并不是所有的参数都在模型中发挥作用,部分参数作用有限、表达冗余,甚至会降低模型的性能。

image-20231030114328061

  • 模型拆分:模型按照计算逻辑拆分成几部分,推理时只需要计算部分模型,其他部分在合适时机提前计算、存储好

框架层加速

使用 tensorRT 等加速计算框架,可以合并网络结构、提高计算的并发度。

硬件加速

硬件加速就是使用 GPU 来进行推理,而不是使用 CPU。

硬件加速并不是万能药,对于简单模型不适合硬件加速,I/O 耗时大于计算耗时。

更快的模型更新

模型热更新时,有模型文件大加载时间长加载/更新时不可用来预测的问题,所以通过加锁的方式解决模型热更新,会造成服务短暂不可用

double buffer 机制

为了解决加锁时服务不可用的问题,所以可以在内存空间中保存两份模型,一份用于正常运行,另一份用于更新模型,模型更新完成后切换新模型进行预测。

online learning 在线学习

在推荐领域经常用到,这里只是提出来不做详细解释。

image-20231030115700729

可维护性 & 开发效率提升

Serving 框架

成熟的 Serving 框架可以简化开发,提供包括但不限于以下:

  • 对外暴露 HTTP/GRPC 接口。
  • 模型版本管理。
  • 动态 batch。

A/B 实验

部署多个模型服务,用于接收 client 的请求,通过流量控制实现 A/B 实验。

image-20231030120530335

MLOps

MLOps = ML + Dev + Ops,MLOps 的原则:

  • Automation:环节尽可能自动化
  • Continuous:模型的持续集成、持续部署、持续监控、持续训练
  • Versioning:代码、数据、模型的版本管理
  • Experiment Tracking:实验记录
  • Testing:数据、模型、应用的自动化测试
  • Monitoring:输入数据、模型输出的监控、降级
  • Reproducibility:减少随机性,保证可复现

image-20231030120327944