我从以下格式获取各种Web应用程序的日志数据:
Session Timestamp Event Parameters
1 1 Started Session
1 2 Logged In Username:"user1"
2 3 Started Session
1 3 Started Challenge title:"Challenge 1", level:"2"
2 4 Logged In Username:"user2"
现在,有人想对此日志数据进行分析(并且希望在适当的转换后将其作为JSON blob接收)。例如,他可能希望接收JSON blob,其中日志数据按Session
分组,TimeFromSessionStart
和CountOfEvents
在数据发送之前添加,以便他可以执行有意义的分析。我应该回复:
[
{
"session":1,"CountOfEvents":3,"Actions":[{"TimeFromSessionStart":0,"Event":"Session Started"}, {"TimeFromSessionStart":1, "Event":"Logged In", "Username":"user1"}, {"TimeFromSessionStart":2, "Event":"Startd Challenge", "title":"Challenge 1", "level":"2" }]
},
{
"session":2, "CountOfEvents":2,"Actions":[{"TimeFromSessionStart":0,"Event":"Session Started"}, {"TimeFromSessionStart":2, "Event":"Logged In", "Username":"user2"}]
}
]
此处,TimeFromSessionStart
,CountOfEvents
等[我们称之为合成附加数据]将不会进行硬编码,我会建立一个网络界面,让人们决定采用何种方式他在JSON blob中需要的合成数据。我想为这个人提供一个很大的灵活性,以决定他想要在JSON blob中使用哪种合成数据。
我希望数据库存储大约1百万行,并在合理的时间内进行转换。
我的问题是关于数据库的选择。使用NoSQL数据库(如MongoDB)使用SQL数据库(例如PostgreSQL v / s)的相对优势和劣势是什么。从我读过的内容到现在,我认为NoSQL可能无法提供足够的灵活性来添加额外的合成数据。另一方面,如果我使用SQL数据库,我可能会面临数据表示的灵活性问题。
我认为MongoDB和PostgreSQL的存储要求是可比较的,因为我必须在两种情况下都建立类似的索引(可能!)以加快查询速度。
如果我使用PostgreSQL,我可以按以下方式存储数据:
Session
和Event
可以是string
,Timestamp
可以是date
,Parameters
可以是hstore
(密钥值对可用于PostgreSQL的)。之后,我可以使用SQL查询来计算合成(或附加)数据,将其临时存储在Rails应用程序中的变量中(它将与PostgreSQL数据库交互并充当想要JSON blob的人的接口)并创建JSON从它那里开始。
另一种可能的方法是使用MongoDB存储日志数据,并使用Mongoid作为Rails应用程序的接口,如果我能够获得足够的灵活性,可以为分析添加额外的合成数据,并对PostgreSQL进行一些性能/存储改进。但是,在这种情况下,我不清楚在MongoDB中存储日志数据的最佳方法是什么。另外,我读到MongoDB会比PostgreSQL慢一些,主要是在后台运行。
编辑: 从我过去几天读到的内容来看,Apache Hadoop似乎也是一个不错的选择,因为它比MongoDB(多线程)更快。
修改 我不是在征求意见,而是想知道使用特定方法的具体优点或缺点。因此,我不认为这个问题是基于意见的。
答案 0 :(得分:2)
你应该从elasticsearch查看logstash / kibana。该堆栈的主要用例是收集日志数据,存储数据,进行分析。
http://www.elasticsearch.org/overview/logstash/
http://www.elasticsearch.org/videos/kibana-logstash/
Mongo也是一个很好的选择,如果您正在考虑自己构建它,但我认为您可以发现来自elasticsearch的产品可以很好地解决您的需求并允许您需要的集成。
答案 1 :(得分:1)
MongoDB非常适合您的任务,其文档存储比刚性SQL表结构更灵活。 下面,请使用Mongoid查找工作测试 这表明你对日志数据输入的理解, 轻松存储为MongoDB集合中的文档, 和使用MongoDB聚合框架的分析。 我已经选择将参数放在子文档中。 这样可以更紧密地匹配样本输入表并简化管道。 生成的JSON略有修改, 但是存在所有指定的计算,数据和分组。 我已经为参数Username添加了一个测试,该测试用于演示subdoc字段的索引。 这适用于您要索引的特定字段, 但是一个完全通用的索引不能在键上完成,你必须重组为值。
我希望这有助于你喜欢它。
测试/单元/ log_data_test.rb
require 'test_helper'
require 'json'
require 'pp'
class LogDataTest < ActiveSupport::TestCase
def setup
LogData.delete_all
@log_data_analysis_pipeline = [
{'$group' => {
'_id' => '$session',
'session' => {'$first' => '$session'},
'CountOfEvents' => {'$sum' => 1},
'timestamp0' => {'$first' => '$timestamp'},
'Actions' => {
'$push' => {
'timestamp' => '$timestamp',
'event' => '$event',
'parameters' => '$parameters'}}}},
{'$project' => {
'_id' => 0,
'session' => '$session',
'CountOfEvents' => '$CountOfEvents',
'Actions' => {
'$map' => { 'input' => "$Actions", 'as' => 'action',
'in' => {
'TimeFromSessionStart' => {
'$subtract' => ['$$action.timestamp', '$timestamp0']},
'event' => '$$action.event',
'parameters' => '$$action.parameters'
}}}}
}
]
@key_names = %w(session timestamp event parameters)
@log_data = <<-EOT.gsub(/^\s+/, '').split(/\n/)
1 1 Started Session
1 2 Logged In Username:"user1"
2 3 Started Session
1 3 Started Challenge title:"Challenge 1", level:"2"
2 4 Logged In Username:"user2"
EOT
docs = @log_data.collect{|line| line_to_doc(line)}
LogData.create(docs)
assert_equal(docs.size, LogData.count)
puts
end
def line_to_doc(line)
doc = Hash[*@key_names.zip(line.split(/ +/)).flatten]
doc['session'] = doc['session'].to_i
doc['timestamp'] = doc['timestamp'].to_i
doc['parameters'] = eval("{#{doc['parameters']}}") if doc['parameters']
doc
end
test "versions" do
puts "Mongoid version: #{Mongoid::VERSION}\nMoped version: #{Moped::VERSION}"
puts "MongoDB version: #{LogData.collection.database.command({:buildinfo => 1})['version']}"
end
test "log data analytics" do
pp LogData.all.to_a
result = LogData.collection.aggregate(@log_data_analysis_pipeline)
json = <<-EOT
[
{
"session":1,"CountOfEvents":3,"Actions":[{"TimeFromSessionStart":0,"Event":"Session Started"}, {"TimeFromSessionStart":1, "Event":"Logged In", "Username":"user1"}, {"TimeFromSessionStart":2, "Event":"Started Challenge", "title":"Challenge 1", "level":"2" }]
},
{
"session":2, "CountOfEvents":2,"Actions":[{"TimeFromSessionStart":0,"Event":"Session Started"}, {"TimeFromSessionStart":2, "Event":"Logged In", "Username":"user2"}]
}
]
EOT
puts JSON.pretty_generate(result)
end
test "explain" do
LogData.collection.indexes.create('parameters.Username' => 1)
pp LogData.collection.find({'parameters.Username' => 'user2'}).to_a
pp LogData.collection.find({'parameters.Username' => 'user2'}).explain['cursor']
end
end
应用程序/模型/ log_data.rb
class LogData
include Mongoid::Document
field :session, type: Integer
field :timestamp, type: Integer
field :event, type: String
field :parameters, type: Hash
end
$ rake test
Run options:
# Running tests:
[1/3] LogDataTest#test_explain
[{"_id"=>"537258257f11ba8f03000005",
"session"=>2,
"timestamp"=>4,
"event"=>"Logged In",
"parameters"=>{"Username"=>"user2"}}]
"BtreeCursor parameters.Username_1"
[2/3] LogDataTest#test_log_data_analytics
[#<LogData _id: 537258257f11ba8f03000006, session: 1, timestamp: 1, event: "Started Session", parameters: nil>,
#<LogData _id: 537258257f11ba8f03000007, session: 1, timestamp: 2, event: "Logged In", parameters: {"Username"=>"user1"}>,
#<LogData _id: 537258257f11ba8f03000008, session: 2, timestamp: 3, event: "Started Session", parameters: nil>,
#<LogData _id: 537258257f11ba8f03000009, session: 1, timestamp: 3, event: "Started Challenge", parameters: {"title"=>"Challenge 1", "level"=>"2"}>,
#<LogData _id: 537258257f11ba8f0300000a, session: 2, timestamp: 4, event: "Logged In", parameters: {"Username"=>"user2"}>]
[
{
"session": 2,
"CountOfEvents": 2,
"Actions": [
{
"TimeFromSessionStart": 0,
"event": "Started Session",
"parameters": null
},
{
"TimeFromSessionStart": 1,
"event": "Logged In",
"parameters": {
"Username": "user2"
}
}
]
},
{
"session": 1,
"CountOfEvents": 3,
"Actions": [
{
"TimeFromSessionStart": 0,
"event": "Started Session",
"parameters": null
},
{
"TimeFromSessionStart": 1,
"event": "Logged In",
"parameters": {
"Username": "user1"
}
},
{
"TimeFromSessionStart": 2,
"event": "Started Challenge",
"parameters": {
"title": "Challenge 1",
"level": "2"
}
}
]
}
]
[3/3] LogDataTest#test_versions
Mongoid version: 3.1.6
Moped version: 1.5.2
MongoDB version: 2.6.1
Finished tests in 0.083465s, 35.9432 tests/s, 35.9432 assertions/s.
3 tests, 3 assertions, 0 failures, 0 errors, 0 skips
答案 2 :(得分:0)
MongoDB是一个理想的数据库。
为原始日志数据创建一个集合。
使用Mongo强大的聚合工具之一,并将聚合数据输出到另一个集合(或多个输出集合,如果您需要不同的存储桶或原始数据视图)
如果您可以容忍响应中的某些延迟,您可以离线执行agg,使用一组预先确定的用户可以提取的可能性,或者按需/临时执行此操作。