DMP系统

  1. DMP(Data Management Platform,数据管理平台
  2. DMP系统广泛应用在互联网的广告定向,个性化推荐
  3. DMP系统会通过处理海量的互联网访问数据以及机器学习算法,给用户标注上各种各样的标签
    • 然后在做个性化推荐和广告投放的时候,再利用这些标签,去做实际的广告排序、推荐等工作
  4. 对于外部使用DMP的系统或者用户来说,可以简单地把DMP看成一个Key-Value数据库
  5. 对Key-Value系统的预期,以广告系统为案例
    • 低响应时间
      • 一般的广告系统留给整个广告投放决策的时间大概是10ms
      • 因此对于访问DMP系统获取用户数据,预期的响应时间都在1ms以内
    • 高可用性
      • DMP系统常用于广告系统,如果DMP系统出问题,意味着在不可用的时间内,整个广告收入是没有的
      • 因此,对于可用性的追求是没有上限的
    • 高并发
      • 如果每天要响应100亿次广告请求,QPS大概是12K
    • 海量数据
      • 如果有10亿个Key,每个用户有500个标签,标签有对应的分数
      • 标签和分数都用4 Bytes的整数来表示,总共大概需要4TB的数据
    • 低成本
      • 广告系统的收入通常用CPM(Cost Per Mille,千次曝光成本)来统计

生成DMP

关注:蓝色的数据管道、绿色的数据仓库KV数据库

数据管道 + 数据仓库

  1. 对于数据管道来说,需要的是高吞吐率,并发量和KV数据库差不多,但不需要低响应时间(1~2秒也是可以接受的)
    • 数据管道的数据读写都是顺序读写,没有大量随机读写的需求
  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硬盘支撑海量的顺序读来节约成本

关系型数据库 – 随机读写

  1. 实现一个最简单的关系型数据库的思路:CSV文件格式,文件里面的每一行就是一条记录
  2. 要修改里面的某一行,就要遍历整个CSV文件,相当于扫描全表,太浪费硬盘的吞吐量,可以考虑加上索引(B+树)
  3. 索引里面没有一整行的数据,只有一个映射关系,这个映射关系可以让行号直接从硬盘的某个位置去读
  4. 索引比数据小很多,可以把索引加载到内存里面
    • 即使不在内存里面,要找数据时快速遍历一下整个索引,也不需要读太多的数据
  5. 索引不仅可以索引行号,还可以索引某个字段
  6. 写入数据
    • 不仅要在数据表里面写入数据,对于所有的索引都需要进行更新
    • 写入一条数据可能就要触发几个随机写入的更新
  7. 查询操作很灵活,无论是那个字段查询,只要有索引,就可以通过一次随机读,很快读到对应的数据
  8. 因此这个数据模型决定了操作会伴随大量的随机读写请求,而随机读写请求,最终是要落到硬盘上的
    • 但HDD的随机IQPS只有100左右,很难做到高并发
  9. 随时添加索引,可以根据任意字段进行查询的灵活性,DMP系统是不太需要的
    • KV数据库:根据主键的随机查询
      • 如果采用上面的方案,会面临大量的随机写入随机读取的挑战
    • 数据管道:只需要不断追加写入顺序读取就好
    • 数据仓库:不需要根据字段进行数据筛选,而是全量扫描数据进行分析汇总

Cassandra – 高并发随机读写

数据模型

  1. Cassandra的一般被称为Row Key,其实是一个16到36字节的字符串
    • 每一个Row Key对应的其实是一个哈希表,里面可以用键值对,再存入其他数据
  2. Cassandra本身不像关系型数据库那样,有严格的Schema,在数据库创建的一开始就定义好有哪些
  3. Cassandra设计了一个叫作列族(Column Family)的概念,把一些需要经常放在一起使用的字段,放在同一个列族里面
    • 保持了不需要严格的Schema的灵活性,也保留了可以把常常在一起使用的数据存放在一起的空间局部性

写操作

  1. Cassandra只有顺序写入,没有随机写入
  2. 写操作包含两个动作
    • 磁盘上写入一条提交日志(Commit Log)
    • 提交日志写成功后,直接在内存的数据结构上更新数据
  3. 每台机器上,都有一个可靠的硬盘让我们去写入提交日志
  4. 写入提交日志都是顺序写,而不是随机写,最大化写入的吞吐量
  5. 内存的空间比较有限
    • 一旦内存里面的数据量或者条目超过了一定的限额,Cassandra会把内存里面的数据结构Dump顺序写)到硬盘上
    • 除了Dump的数据结构文件,Cassandra还会根据Row Key来生成一个索引文件,方便后续基于索引来进行快速查询
  6. 随着硬盘上Dump出来的文件越来越多,Cassandra会在后台进行文件的对比合并(Compaction)
    • 合并:顺序读取多个文件,在内存里面完成合并,再Dump出来一个新文件
    • 整个操作过程,在硬盘层面仍然是顺序读写

读操作

  1. 读请求会通过缓存BloomFilter进行两道过滤,尽可能避免数据请求命中硬盘
  2. 从Cassandra读数据时,会先从内存里面找数据,再从硬盘读数据,然后把两部分的数据合并成最终结果
  3. 硬盘上的文件,在内存里面会有对应的Cache,只有在Cache里面找不到,才会去请求硬盘里面的数据
  4. 如果不得不访问硬盘,因为硬盘里可能Dump了很多不同时间点的内存数据的快照,查找顺序:新 -> 旧
    • 带来另一个问题:可能要查询很多个Dump文件,才能找到需要的数据
    • Cassandra的优化方案:布隆过滤器
      • 为每一个Dump文件里面的所有Row Key生成一个BloomFilter,然后把这个BloomFilter放在内存里面
      • 如果要查询的Row Key不在某个Dump文件里面,那么99%以上的情况,会被BloomFilter过滤掉,不需要访问硬盘

SSD

  1. Cassandra的数据写入都是Commit Log的顺序写入,即不断地往硬盘后面追加内容,而不是去修改现有的文件内容
  2. 一旦内存里面的数据超过一定的阈值,Cassandra会完整地Dump一个新文件到文件系统上,同样是一个追加写入
  3. 数据的对比和合并,同样是读取现有的多个文件,然后写一个新的文件
  4. 写入操作只追加不修改的特性,正好天然符合SSD硬盘只能按块擦除的特性
    • Cassandra用到的SSD,不需要频繁地进行后台的Compaction,能够最大化SSD的使用寿命

参考资料

深入浅出计算机组成原理