日志数据分析:数据库的选择

时间:2014-05-08 14:23:31

标签: ruby-on-rails mongodb postgresql mapreduce relational-database

我从以下格式获取各种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分组,TimeFromSessionStartCountOfEvents在数据发送之前添加,以便他可以执行有意义的分析。我应该回复:

[
  {
    "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"}] 
  }
]

此处,TimeFromSessionStartCountOfEvents等[我们称之为合成附加数据]将不会进行硬编码,我会建立一个网络界面,让人们决定采用何种方式他在JSON blob中需要的合成数据。我想为这个人提供一个很大的灵活性,以决定他想要在JSON blob中使用哪种合成数据。

我希望数据库存储大约1百万行,并在合理的时间内进行转换。

我的问题是关于数据库的选择。使用NoSQL数据库(如MongoDB)使用SQL数据库(例如PostgreSQL v / s)的相对优势和劣势是什么。从我读过的内容到现在,我认为NoSQL可能无法提供足够的灵活性来添加额外的合成数据。另一方面,如果我使用SQL数据库,我可能会面临数据表示的灵活性问题。

我认为MongoDB和PostgreSQL的存储要求是可比较的,因为我必须在两种情况下都建立类似的索引(可能!)以加快查询速度。

如果我使用PostgreSQL,我可以按以下方式存储数据: SessionEvent可以是stringTimestamp可以是dateParameters可以是hstore(密钥值对可用于PostgreSQL的)。之后,我可以使用SQL查询来计算合成(或附加)数据,将其临时存储在Rails应用程序中的变量中(它将与PostgreSQL数据库交互并充当想要JSON blob的人的接口)并创建JSON从它那里开始。

另一种可能的方法是使用MongoDB存储日志数据,并使用Mongoid作为Rails应用程序的接口,如果我能够获得足够的灵活性,可以为分析添加额外的合成数据,并对PostgreSQL进行一些性能/存储改进。但是,在这种情况下,我不清楚在MongoDB中存储日志数据的最佳方法是什么。另外,我读到MongoDB会比PostgreSQL慢一些,主要是在后台运行。

编辑: 从我过去几天读到的内容来看,Apache Hadoop似乎也是一个不错的选择,因为它比MongoDB(多线程)更快。

修改 我不是在征求意见,而是想知道使用特定方法的具体优点或缺点。因此,我不认为这个问题是基于意见的。

3 个答案:

答案 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是一个理想的数据库。

  1. 为原始日志数据创建一个集合。

  2. 使用Mongo强大的聚合工具之一,并将聚合数据输出到另一个集合(或多个输出集合,如果您需要不同的存储桶或原始数据视图)

  3. 如果您可以容忍响应中的某些延迟,您可以离线执行agg,使用一组预先确定的用户可以提取的可能性,或者按需/临时执行此操作。

    http://docs.mongodb.org/manual/aggregation/