假设我们有以下JSON事件数据示例:
{
"eventId":"eb1363c3-6bf7-4a42-9daa-66270b922367",
"timestamp":"2014-10-28T09:12:22.628Z",
"ip":"1.2.3.4",
"device":{
"type":"mobile",
"os":{
"name":"iOS",
"version":"7.1.1"
},
"name":"iPhone 4/4s",
...
},
"eventType":"AddedProductToCart",
"store":"US",
"product":{
"sku":"ABC123",
"name":"Yellow Socks",
"quantity":1,
"properties":{
"foo":"bar",
"bar":1
}
...
},
"user":{
"id":123456,
"name":"jeff",
"type":"registered"
...
}
}
虽然总是提供"eventId"one_answers"timestamp",但数组的结构可能会有所不同,也不相同。大约有30-40种独特的事件类型,它们都具有不同的事件属性。大多数事件数据都具有嵌套结构。
存储这些事件属性的最佳方法是什么?我研究了MongoDB、DynamoDB和一个名为EventStore的项目(http://geteventstore.com)。显然,我也考虑过MySQL,但我想知道它在我们的用例中会如何执行。
数据的存储只是第一部分。在这之后,我们应该能够用下面这样的复杂查询来查询我们的数据库/事件存储(例如,不仅仅是通过索引ID检索):
select all events where eventType is "AddedProductToCart" and timestamp > 2 weeks ago
-> should return all "AddedProductToCart" from 2 weeks ago until now
select all events where device.OS.name is "iOS" and device.OS.version is "7.1.1"
-> should return all events from iOS 7.1.1
等等。
我们预计每月约有1000万场活动。这相当于平均每秒3-4次写入,可能更像是峰值/最坏情况下每秒30-40次写入。存储应该不是一个真正的问题——每个事件的总大小可能不会超过1或2kb(相当于每100万个事件1-2GB)。
查询部分最好使用PHP。例如,DynamoDB有一个用于PHP的SDK,这肯定会促进我们的
我们对此的最佳解决方案是什么?写入速度应该很快,我们的查询也应该是可接受的。简而言之,我们正在寻找一个低成本的数据存储,以便轻松存储和检索(->不仅使用索引查询,还使用嵌套JSON中的事件属性查询)我们的数据。
感谢您的任何建议,如果需要更多信息来正确回答这个问题,我很乐意提供更多信息。
亚马逊的DynamoDB提供了一个完全管理(自动扩展)、持久和可预测的解决方案。
从您期望的流量和数据量来看,DynamoDB的25个写/读容量单元和25 GB的免费层基本上免费覆盖了您的操作。
每个写入容量单位相当于写入1KB的数据,因此,如果您希望每秒写入3-4次2KB的数据时,则需要提供8个WCU。此外,DynamoDB的性能非常可预测,具有快速的个位数毫秒延迟。有关免费层的更多信息,请查看http://aws.amazon.com/dynamodb/pricing/.
就数据集而言,对于非文档对象,使用全局二级索引进行查询相对简单。
这里有一个PHP SDK的例子
$twoWeeksAgo = date("Y-m-d H:i:s", strtotime("-14 days"));
$response = $dynamoDB->query(array(
"TableName" => <Table Name>,
"KeyConditions => array(
"EventType" => array(
"ComparisonOperator" => ComparisonOperator::EQ,
"AttributeValueList" => array(
array(Type::STRING => "AddedProductToCart")
)
),
"Timestamp" => array(
"ComparisonOperator" => ComparisonOperator:GE,
"AttributeValueList" => array(
array(Type::STRING => $twoWeeksAgo)
)
)
)
));
您可以通过扫描查询"Device.OS.Name"one_answers"Device.OS.Version",但您应该根据要进行的查询类型考虑一些优化。
如果您希望进行临时查询,可以进行并行扫描调用,然后在嵌套属性上使用ConditionalExpression应用ScanFilter。通过并行化扫描,可以优化表上读取容量单位的消耗以及操作速度。有关并行扫描的更多信息,请查看http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/QueryAndScan.html#QueryAndScanParallelScan.
或者,如果您有要查询的select属性,可以考虑将一些字段设置为顶级属性,或者将它们移动到自己的单独表中,展平必要的属性(即os.name到osname),并对原始项进行反向引用(主要适用于"设备"等文档)。通过这样做,您可以在这些属性之上添加索引,并快速高效地查询它们。此外,随着在线索引的提前发布,您应该能够在必要时添加和删除索引,以尽快满足您的需求。
如果你想更详细地讨论这个问题,或者问一些关于使用DynamoDB的问题,请随时通过私人信息联系我。
感谢
MongoDB是一个不错的选择。它可以很容易地处理写操作(mongod
在我的笔记本电脑上看到了更多的操作)。
你提到的问题都是基本问题。例如:
db.collection.find({"device.OS.name":"iOS","device.OS.version":"7.1.1"})
和(为便于阅读而缩短)
db.collection.find({"eventType":"AddedProductToCart",timestamp:{$gte: ISODate(iso8601String)}})
如果指数设置正确,这些指数应该是闪电般的快。您甚至可以使用TTL索引来自动删除某个时间以前的事件。
对于数据分析,您既有map/reduce,也有MongoDB极其强大的聚合框架。
让我们来看看缺点。虽然MongoDB的扩展相对容易,但出于某种原因,人们认为具有自动分发数据的复制分片集群与MongoDB的其他集群一样容易管理。关键词是相对来说很容易(将其与MySQL或-Lord help us-Oracle的复制数据分区进行比较),但它仍然存在一些陷阱。
在不使用MMS的分片环境中进行时间点恢复是可能的,但您确实必须知道自己在做什么,因为分片的单个备份的同步非常棘手。
无论你选择哪个数据库,我强烈建议你与相应的专家联系。生产数据是基本的,不应由非专业人员规划和维护任何数据库。