计算机组成 -- DMP
DMP系统
- DMP(Data Management Platform,数据管理平台)
- DMP系统广泛应用在互联网的广告定向,个性化推荐
- DMP系统会通过处理海量的互联网访问数据以及机器学习算法,给用户标注上各种各样的标签
- 然后在做个性化推荐和广告投放的时候,再利用这些标签,去做实际的广告排序、推荐等工作
- 对于外部使用DMP的系统或者用户来说,可以简单地把DMP看成一个Key-Value数据库
- 对Key-Value系统的预期,以广告系统为案例
- 低响应时间
- 一般的广告系统留给整个广告投放决策的时间大概是10ms
- 因此对于访问DMP系统获取用户数据,预期的响应时间都在1ms以内
- 高可用性
- DMP系统常用于广告系统,如果DMP系统出问题,意味着在不可用的时间内,整个广告收入是没有的
- 因此,对于可用性的追求是没有上限的
- 高并发
- 如果每天要响应100亿次广告请求,QPS大概是12K
- 海量数据
- 如果有10亿个Key,每个用户有500个标签,标签有对应的分数
- 标签和分数都用4 Bytes的整数来表示,总共大概需要4TB的数据
- 低成本
- 广告系统的收入通常用CPM(Cost Per Mille,千次曝光成本)来统计
- 低响应时间
生成DMP
关注:蓝色的数据管道、绿色的数据仓库、KV数据库
数据管道 + 数据仓库
- 对于数据管道来说,需要的是高吞吐率,并发量和KV数据库差不多,但不需要低响应时间(1~2秒也是可以接受的)
- 数据管道的数据读写都是顺序读写,没有大量随机读写的需求
- 数据仓库的数据读取的量要比数据管道大得多(数据分析)
技术选型
数据存储 | KV数据库 | 数据管道 | 数据仓库 |
---|---|---|---|
响应时间 | 1ms | 1-2s | 10分钟~几小时 |
并发请求 | 10W~100W | 10W~100W | 100~1W |
存储数据量 | 100TB~1PB | 10TB~1PB | 5PB~100PB |
读写比例 | 随机读、随机写 | 顺序读、顺序写 | 顺序读、顺序写 |
存储成本 | 重要 | 不重要 | 重要 |
可用性要求 | 高 | 高 | 低 |
易失性要求 | 低 | 高 | 高 |
MongoDB的缺陷 | 没有针对SSD特性进行设计,无法做到极限的高并发读取 | 没有针对顺序写入和吞吐率进行优化,更多考虑数据库的随机读写 | 没有Scheme导致需要在数据里面保留元信息,会占用更多存储空间 |
实际选型 | AeroSpike | Kafka | Hadoop/Hive |
选型原因 | 针对SSD设计,并发请求性能好,成本远低于使用内存 | 使用Zero-Copy和DMA,最大化吞吐量 | 使用Avro/Thrift/ProtoBuffer序列化,节约存储空间,使用HDD硬盘支撑海量的顺序读来节约成本 |
关系型数据库 – 随机读写
- 实现一个最简单的关系型数据库的思路:CSV文件格式,文件里面的每一行就是一条记录
- 要修改里面的某一行,就要遍历整个CSV文件,相当于扫描全表,太浪费硬盘的吞吐量,可以考虑加上索引(B+树)
- 索引里面没有一整行的数据,只有一个映射关系,这个映射关系可以让行号直接从硬盘的某个位置去读
- 索引比数据小很多,可以把索引加载到内存里面
- 即使不在内存里面,要找数据时快速遍历一下整个索引,也不需要读太多的数据
- 索引不仅可以索引行号,还可以索引某个字段
- 写入数据
- 不仅要在数据表里面写入数据,对于所有的索引都需要进行更新
- 写入一条数据可能就要触发几个随机写入的更新
- 查询操作很灵活,无论是那个字段查询,只要有索引,就可以通过一次随机读,很快读到对应的数据
- 因此这个数据模型决定了操作会伴随大量的随机读写请求,而随机读写请求,最终是要落到硬盘上的
- 但HDD的随机IQPS只有100左右,很难做到高并发
- 随时添加索引,可以根据任意字段进行查询的灵活性,DMP系统是不太需要的
- KV数据库:根据主键的随机查询
- 如果采用上面的方案,会面临大量的随机写入和随机读取的挑战
- 数据管道:只需要不断追加写入和顺序读取就好
- 数据仓库:不需要根据字段进行数据筛选,而是全量扫描数据进行分析汇总
- KV数据库:根据主键的随机查询
Cassandra – 高并发随机读写
数据模型
- Cassandra的键一般被称为Row Key,其实是一个16到36字节的字符串
- 每一个Row Key对应的值其实是一个哈希表,里面可以用键值对,再存入其他数据
- Cassandra本身不像关系型数据库那样,有严格的Schema,在数据库创建的一开始就定义好有哪些列
- Cassandra设计了一个叫作列族(Column Family)的概念,把一些需要经常放在一起使用的字段,放在同一个列族里面
- 保持了不需要严格的Schema的灵活性,也保留了可以把常常在一起使用的数据存放在一起的空间局部性
写操作
- Cassandra只有顺序写入,没有随机写入
- 写操作包含两个动作
- 往磁盘上写入一条提交日志(Commit Log)
- 提交日志写成功后,直接在内存的数据结构上更新数据
- 每台机器上,都有一个可靠的硬盘让我们去写入提交日志
- 写入提交日志都是顺序写,而不是随机写,最大化写入的吞吐量
- 内存的空间比较有限
- 一旦内存里面的数据量或者条目超过了一定的限额,Cassandra会把内存里面的数据结构Dump(顺序写)到硬盘上
- 除了Dump的数据结构文件,Cassandra还会根据Row Key来生成一个索引文件,方便后续基于索引来进行快速查询
- 随着硬盘上Dump出来的文件越来越多,Cassandra会在后台进行文件的对比合并(Compaction)
- 合并:顺序读取多个文件,在内存里面完成合并,再Dump出来一个新文件
- 整个操作过程,在硬盘层面仍然是顺序读写
读操作
- 读请求会通过缓存、BloomFilter进行两道过滤,尽可能避免数据请求命中硬盘
- 从Cassandra读数据时,会先从内存里面找数据,再从硬盘读数据,然后把两部分的数据合并成最终结果
- 硬盘上的文件,在内存里面会有对应的Cache,只有在Cache里面找不到,才会去请求硬盘里面的数据
- 如果不得不访问硬盘,因为硬盘里可能Dump了很多不同时间点的内存数据的快照,查找顺序:新 -> 旧
- 带来另一个问题:可能要查询很多个Dump文件,才能找到需要的数据
- Cassandra的优化方案:布隆过滤器
- 为每一个Dump文件里面的所有Row Key生成一个BloomFilter,然后把这个BloomFilter放在内存里面
- 如果要查询的Row Key不在某个Dump文件里面,那么99%以上的情况,会被BloomFilter过滤掉,不需要访问硬盘
SSD
- Cassandra的数据写入都是Commit Log的顺序写入,即不断地往硬盘后面追加内容,而不是去修改现有的文件内容
- 一旦内存里面的数据超过一定的阈值,Cassandra会完整地Dump一个新文件到文件系统上,同样是一个追加写入
- 数据的对比和合并,同样是读取现有的多个文件,然后写一个新的文件
- 写入操作只追加不修改的特性,正好天然符合SSD硬盘只能按块擦除的特性
- Cassandra用到的SSD,不需要频繁地进行后台的Compaction,能够最大化SSD的使用寿命
参考资料
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.