用户增长 实时归因实现 (2) 行为模块 点击模块 归因模块
用户增长 实时归因实现 (1) 配置模块 去重模块 监控数据
用户增长 实时归因的重构设计方案
用户增长 归因点击数据获取
Redis学习 (11) 新数据类型
本节介绍一些在 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学习 (10) 过期删除与内存淘汰
过期删除
Redis 可以设置 key 的过期时间,对于过期的 key,Redis 需要进行清理。
过期时间
当对一个 key 设置了过期时间,Redis 会把该 key 带上过期时间存储到一个过期字典(哈希表)中,过期字典保存了所有 key 的过期时间。所以在访问一个 key 时,可以在常数级时间复杂度查找 key 是否过期:
- 不在过期字典中,说明没有设置过期时间。
- 在过期字典中,获取过期时间,与系统当前时间进行比对是否过期。
删除策略
Redis 采用惰性删除+定期删除两种删除策略配合使用,以求在合理使用 CPU 时间和避免内存浪费之间取得平衡。
惰性删除
惰性删除中,不主动删除过期 key,只有当查询到过期 key 时才进行删除操作,删除可以选择异步删除或者同步删除。
惰性删除节省 CPU 时间,但是浪费内存。
定期删除
定期删除策略的做法是,每隔一段时间随机从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。
定期删除相对于惰性删除更需要 CPU 资源,但是更加节约内存。
Redis 实现的定期删除需要解决两个问题:
- 定期检查间隔时间:默认每 10 秒钟检查一次,这是可以配置的。
- 随机抽查的数量:数量是写死在代码中的,数值是 20。
Redis 的定期删除的流程:
- 从过期字典中随机抽取 20 个 key;
- 检查这 20 个 key 是否过期,并删除已过期的 key;
- 如果本轮检查的已过期 key 的数量,比例大于 25%(5 个),则继续重复步骤 1;否则停止继续删除过期 key,然后等待下一轮再检查。
Redis 为了保证定期删除不会出现循环过度,导致线程卡死现象,为此增加了定期删除循环流程的时间上限,默认不会超过 25ms。
Redis学习 (9) 线程模型
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。
MySQL高级 (12) buffer pool 详解
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 除了缓存数据页和索引页,还包括了回滚页、插入换成、自适应哈希索引、锁信息等。
模型是如何部署在线上的
模型部署按照部署设备分类,主要分为两类:
- 云端/服务器上的部署,将模型作为一个线上服务进行部署。
- 移动端/设备端上的部署,就是将模型作为 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 网络大部分节点都参与运算。
dense 网络每次计算都用到,且占用空间小,不适合独立存储;sparse 网络每次计算只用到很少一部分参数,适合独立存储。
所以将 dense 网络存储在内存中,sparse 网络以分布式 kv 的方式进行存储。
压缩模型
多用于 NLP、CV 等领域,可以使用:
- 模型量化(把模型从浮点数变为整数 INT8)。
- 蒸馏(将复杂的网络特征提取出来,迁移到参数量小的网络中)。
压缩模型会使得模型的效果变差,要在模型效果和计算速度上进行 balance。
更快的推理速度
模型加速推理的优化方向:
- 算法层加速:
- 训练前:选用轻量级的网络。
- 训练后加速。
- 框架层加速:tensorRT 等。
- 硬件层加速:使用 GPU 而不是 CPU。
算法层加速 - 训练后加速
训练后加速方法:
- 模型量化。
- 模型蒸馏。
- 模型剪枝:去掉模型中不重要的参数。
模型剪枝可行性:研究表明,并不是所有的参数都在模型中发挥作用,部分参数作用有限、表达冗余,甚至会降低模型的性能。
- 模型拆分:模型按照计算逻辑拆分成几部分,推理时只需要计算部分模型,其他部分在合适时机提前计算、存储好。
框架层加速
使用 tensorRT 等加速计算框架,可以合并网络结构、提高计算的并发度。
硬件加速
硬件加速就是使用 GPU 来进行推理,而不是使用 CPU。
硬件加速并不是万能药,对于简单模型不适合硬件加速,I/O 耗时大于计算耗时。
更快的模型更新
模型热更新时,有模型文件大,加载时间长,加载/更新时不可用来预测的问题,所以通过加锁的方式解决模型热更新,会造成服务短暂不可用。
double buffer 机制
为了解决加锁时服务不可用的问题,所以可以在内存空间中保存两份模型,一份用于正常运行,另一份用于更新模型,模型更新完成后切换新模型进行预测。
online learning 在线学习
在推荐领域经常用到,这里只是提出来不做详细解释。
可维护性 & 开发效率提升
Serving 框架
成熟的 Serving 框架可以简化开发,提供包括但不限于以下:
- 对外暴露 HTTP/GRPC 接口。
- 模型版本管理。
- 动态 batch。
A/B 实验
部署多个模型服务,用于接收 client 的请求,通过流量控制实现 A/B 实验。
MLOps
MLOps = ML + Dev + Ops,MLOps 的原则:
- Automation:环节尽可能自动化
- Continuous:模型的持续集成、持续部署、持续监控、持续训练
- Versioning:代码、数据、模型的版本管理
- Experiment Tracking:实验记录
- Testing:数据、模型、应用的自动化测试
- Monitoring:输入数据、模型输出的监控、降级
- Reproducibility:减少随机性,保证可复现