用于存储点击日志的Cassandra

时间:2017-01-18 07:53:16

标签: cassandra distributed-computing nosql

我在ad tech工作,我们当前的基础架构使用MySQL来存储点击和转换日志。到目前为止,MySQL对我们运行针对点击数据的即席查询非常有用。 我们正在考虑切换到Cassandra,因为我们在高峰时段收到巨大的流量峰值。不仅如此,我们正在以非常快的速度增长,我们偶尔每秒获得大约500-1000次点击(持续时间延长,有时持续20-30分钟)。 我一直是可用的选择,到目前为止,我的研究让我相信在写性能方面没有什么比Cassandra更好。 我目前正在创建用于存储点击次数的数据模型。 任何点击的主要组成部分如下:

  1. 广告系列ID
  2. Pub id
  3. 时间戳
  4. 广告素材ID
  5. 事件代码(无论是有效点击还是无效点击。这是一个int值。例如,event_code = 0是有效点击)
  6. 现在,我需要支持以下查询:

     1. SELECT * FROM clicks WHERE campaign_id=?
     2. SELECT * FROM clicks WHERE campaign_id=? AND date_time>=? AND date_time <=? 
     3. SELECT * FROM clicks WHERE campaign_id=? AND pub_id=? AND  AND date_time>=? AND date_time <=?  AND event_code=?
    

    等 这很简单,可以使用MySQL,之后我只是从csv文件中获取这些查询的所有数据。 但是,如果我要根据第一个查询对表进行建模,这意味着我需要在Cassandra中创建一个表,如下所示:

        CREATE TABLE clicks_by_campaign(
         camp_id int,
         pub_id int,
         date_time timestamp,
         creative_id int,
         event_code int,
        //other fields like ip, user agent ,device etc,
        PRIMARY KEY(camp_id,pub_id,date_time,event_code,creative_id))
    

    但是有些广告系列可以拥有数百万行。例如,我们的广告系列具有特定ID,例如id = 3,其点击次数超过700万次。 这不会产生宽行问题吗?据我所知,所有这些活动数据都将作为一个分区存储在一台物理机器上。我的想法在这里是正确的还是我错过了什么?请注意,还必须支持其他查询。例如,我可能必须共享特定发布者的点击日志(与广告系列ID无关)。在这种情况下,查询将如下所示:

    SELECT * FROM clicks_by_publisher WHERE pub_id=? 
    

    这显然意味着我必须以'clicks_by_publisher'等名称创建另一个表。

    我还想指出,我将使用Apache Flink在1分钟的时间窗口内分析,汇总和分组点击信息。这些结果将进一步存储到MySQL中,以尽可能多地支持即席查询。

    无论如何,如果有人指出我正确的方向,我将不胜感激。 我还可以使用其他策略吗?我错过了什么吗? 谢谢:))

2 个答案:

答案 0 :(得分:1)

您有几个选择。三,我觉得我可以形容。第一个是按如下方式指定列

campaign_id = PRIMARY_KEY
event_code = CLUSTER_KEY
date_time = CLUSTER_KEY

可以在群集密钥上运行大于或等于查询。您的查询将会运行。

你说这会为每个广告系列ID创建一个分区。要解决存储在一台物理计算机上的行,您可以创建一个不同的表,将活动ID与点击表中的行ID相关联。这会减少存储在一台机器上的整体数据。

另一个解决方案是为每个广告系列ID添加一个机器ID。这会平均分配每台机器之间的行数。这意味着为每个查询创建一个前缀为每个机器ID的查询,但允许增长。

这会导致spark。 Spark将处理在多台机器上运行您的查询并自动连接结果,基本上按照上面描述的方式执行,而不会产生开发开销。

我自己与Cassandra合作,我选择了第一种和第二种解决方案的组合,因为它符合我正在使用的数据结构。请记住,Cassandra在写入时非常有效,所以不要过于保守地创建表来帮助过滤查询,并且更加稀疏地存储数据。

通过以日期为前缀的广告系列ID的哈希值来存储点击次数可能对您有用。 编辑:除非禁用,否则Cassandra将使用Murmur3算法自动哈希主键。

答案 1 :(得分:1)

要模拟快速读取和分布式权限的要求,请使用下表定义 -

CREATE TABLE clicks_by_campaign(
 camp_id int,
 createdon bigint,
 pub_id int,
 creative_id int,
 event_code int,
//other fields like ip, user agent ,device etc,
PRIMARY KEY((camp_id,createdon),event_code))

这有助于在分区之间均匀分布数据。这也将解决我们的第二和第三个问题 -

2. SELECT * FROM clicks WHERE campaign_id=? AND date_time>=? AND date_time <=? 
  Query will be - 
 SELECT * FROM clicks_by_campaign WHERE token(camp_id, createdon) > token(100, '1111111111111') AND token(camp_id, createdon) <= token(100, '22222222222222')


 3. SELECT * FROM clicks WHERE campaign_id=? AND pub_id=? AND  AND date_time>=? AND date_time <=?  AND event_code=?
 The query will be - 
SELECT * FROM clicks_by_campaign WHERE token(camp_id, createdon) > token(100, '1111111111111') AND token(camp_id, createdon) <= token(100, '22222222222222') AND event_code=10

第一次查询 -

1. SELECT * FROM clicks WHERE campaign_id=?

这实际上是cassandra的反模式。我会做什么,分批处理活动数据,每小时 - 每周 - 每周 - 每年。再次考虑广告系列ID,我们是否必须一次处理所有数据。 “clicks_by_publisher”也是如此。

编辑1

Could you elaborate on what you mean by 'token' ? 

Cassandra使用分区键对行进行分区。在上面的表定义中,我们结合了camp_id和createdon值(camp_id并创建了类似于RDBMS中的composit主键)来形成分区键。 cassandra分区器计算组合camp_id和createdon的哈希值,并确定该行所在的分区。要检索相同的行,分区程序需要重新计算哈希值。函数toke(),就是这样。

时间戳表示发生点击事件的时间,此值以毫秒为单位。使用createdon(long long)将有助于在分区之间均匀分布行。

例如对于插入语句

1. INSERT INTO clicks_by_campaign (camp_id,createdon ,....) values 100,1111111111111,......) the calculated hash, lets say 111 (combining values 100,1111111111111 )  -- this will go in partition 1 
2. INSERT INTO clicks_by_campaign (camp_id,createdon ,....) values (100,2222222222222,......) the calculated hash, lets say 222 (combining values 100,2222222222222 )   -- this will go in partition 2

Java具有将日期转换为毫秒的API。以毫秒为单位的日期可以使用任何时区转换为任何格式。

事实上,您的用例是设计时间序列数据模型的合适人选。