Giới thiệu

Apache Druid là kho lưu trữ dữ liệu phân tích hiệu suất cao cho dữ liệu theo hướng sự kiện. Druid dựa vào hệ thống tệp phân tán hoặc lưu trữ đối tượng nhị phân để lưu trữ dữ liệu. Druid được thiết kế để nhanh chóng nhập số lượng lớn dữ liệu sự kiện và cung cấp các truy vấn có độ trễ thấp trên đầu dữ liệu.

Các ưu điểm của Druid

  • Sub-second OLAP Queries: Kiến trúc độc đáo của Druid cho phép lọc đa chiều nhanh chóng, các nhóm thuộc tính đặc biệt và tổng hợp cực nhanh.
  • Truyền Real-time Streaming: Druid sử dụng tính năng khóa - giải phóng (lock -free) để cho phép nhập đồng thời và truy vấn các tập dữ liệu khối lượng lớn, nhiều chiều. Khám phá các sự kiện ngay sau khi chúng xảy ra.
  • Ứng dụng phân tích mạnh mẽ: Druid có nhiều tính năng được xây dựng cho multi-tenancy. Có thể đáp ứng hàng ngàn người dùng đồng thời.
  • Chi phí hiệu quả Druid cực kỳ hiệu quả về quy mô và có nhiều tính năng được tích hợp để giảm chi phí. Đánh đổi chi phí và hiệu suất với các nút cấu hình đơn giản.
  • Druid có tính sẵn sàng cao được sử dụng để hỗ trợ các triển khai SaaS cần được cập nhật mọi lúc. Druid hỗ trợ cập nhật rolling dần dần  để dữ liệu của bạn vẫn có sẵn và có thể truy vấn trong quá trình cập nhật phần mềm. Quy mô lên hoặc xuống mà không mất dữ liệu.
  • Các triển khai Druid hiện tại có thể mở rộng xử lý hàng nghìn tỷ sự kiện, petabyte dữ liệu và hàng ngàn truy vấn mỗi giây.

https://druid.apache.org/

Khi nào nên sử dụng Druid

Các tổ chức đã triển khai Druid để phân tích các sự kiện của người dùng, server và thị trường trên nhiều ngành công nghiệp bao gồm truyền thông, viễn thông, bảo mật, ngân hàng, chăm sóc sức khỏe và bán lẻ. Druid rất phù hợp nếu bạn có các yêu cầu sau:

  • Bạn đang xây dựng một ứng dụng yêu cầu tổng hợp nhanh và truy vấn OLAP
  • Bạn muốn làm phân tích real time
  • Bạn có rất nhiều dữ liệu (hàng nghìn tỷ sự kiện, petabyte dữ liệu)
  • Bạn cần một kho lưu trữ dữ liệu luôn có sẵn

Có 2 cách triển khai Druid phổ biến hiện nay là S3 (phổ biến cho những người trên AWS) và HDFS (phổ biến nếu bạn đã triển khai Hadoop). Trong bài đăng này, tôi sẽ chỉ cho bạn cách định cấu hình Druid trên Hadoop.

Các trang tài liệu liên quan đến Druid

Type of InformationResourcesDescription
Introductions

About Druid (Source: druid.io)

Introduces the feature highlights of Druid, and explains in which environments the data store is best suited. The page also links to comparisons of Druid against other common data stores.

Druid Concepts

(Source: druid.io)

This page is the portal to the druid.io technical documentation. While the body of this page describes some of the main technical concepts and components, the right-side navigation pane outlines and links to the topics in the druid.io documentation.

Druid: A Real-time Analytical Data Store

(Source: druid.io)

This white paper describes the Druid architecture in detail, performance benchmarks, and an overview of Druid issues in a production environment. The extensive References section at the end of the document point to a wide range of information sources.

Tutorial

Druid Quickstart

(Source: druid.io)

A getting started tutorial that walks you through a Druid package, installation, and loading and querying data. The installation of this tutorial is for instructional purposes only and not intended for use in a Hortonworks Hadoop cluster.

Developing on Druid

Developing on Druid

(Source: druid.io)

Provides an overview of major Druid components to help developers who want to code applications that use Druid-ingested data. The web page links to another about segments, which is an essential entity to understand when writing applications for Druid.

Data Ingestion

Batch Data Ingestion

Loading Streams

(Source: druid.io)

These two pages introduce how Druid can ingest data from both static files and real-time streams.

Queries of Druid Data

Querying

(Source: druid.io)

Describes the method for constructing queries, supported query types, and query error messages.

Best Practices

Recommendations

(Source: druid.io)

A list of tips and FAQs.

Cài đặt cấu hình

Install Druid

Yêu cầu:

  • Một cụm server đã được cài HDB 3 trở lên
  • Một server đã dược cài MySQL hoặc PostgreSQL (trong bài mình sẽ dùng MySQL)

Trên màn hình quản lý ambari chọn + Add service trên menu sau đó tích vào Druid sau chọn Slave và Client

Cấu hình cho Druid

Để có thể dễ mở rộng và tránh mất mát metastore nên chọn một hệ quản trị cơ sở liệu làm metastore.

Tạo user và database cho metastore

# mysql -u root -p

CREATE DATABASE <DRUIDDATABASE> DEFAULT CHARACTER SET utf8;
# mysql -u root -p

CREATE USER '<DRUIDUSER>'@'%' IDENTIFIED BY '<DRUIDPASSWORD>';

GRANT ALL PRIVILEGES ON <DRUIDDATABASE>.* TO '<DRUIDUSER>'@'%’;

FLUSH PRIVILEGES;

Trên ambari vào druid config và điền các thông tin như mysql host, user, database

Sau đó restart lại Druid

Bài toán thực hành trên Druid

Load dữ liệu test (Wikiticker JSON)

Trong thư mục druid của HDB đã có sẵn dữ liệu dưới dạng JSON để test.

/usr/hdp/3.0.1.0-187/druid/quickstart/wikiticker-2015-09-12-sampled.json.gz

Mỗi bản ghi có dạng như sau:

{
    "time":"2015-09-12T00:47:05.474Z",
    "channel":"#en.wikipedia",
    "cityName":"Auburn",
    "comment":"/* Status of peremptory norms under international law */ fixed spelling of 'Wimbledon'",
    "countryIsoCode":"AU",
    "countryName":"Australia",
    "isAnonymous":true,
    "isMinor":false,
    "isNew":false,
    "isRobot":false,
    "isUnpatrolled":false,
    "metroCode":null,
    "namespace":"Main",
    "page":"Peremptory norm",
    "regionIsoCode":"NSW",
    "regionName":"New South Wales",
    "user":"60.225.66.142",
    "delta":0,
    "added":0,
    "deleted":0
}

Mỗi hàng trong tập dữ liệu sẽ có cùng các khóa như trên với các giá trị khác nhau. Hãy phân tách timestamp của chúng tôi (thuộc tính unique duy nhất), dimensions (kiểu String) và metrics (kiểu number) vào các nhóm riêng của chúng:

timestamp
"time"

timestamp có thể được tìm thấy trong trường time . Nếu tập dữ liệu của bạn không có trường time, bạn có thể gắn thẻ tất cả các hàng bằng timestamp cố định "2000-01-01T00: 00: 00.000Z" hoặc bạn có thể chèn thời gian hiện tại bằng ngôn ngữ lập trình yêu thích của mình.

dimensions

  "channel",
  "cityName",
  "comment",
  "countryIsoCode",
  "countryName",
  "isAnonymous",
  "isMinor",
  "isNew",
  "isRobot",
  "isUnpatrolled",
  "metroCode",
  "namespace",
  "page",
  "regionIsoCode",
  "regionName",
  "user"

Tất cả các trường trên đều có kiểu string

metrics

{
  "name" : "count",
  "type" : "count"
},
{
  "name" : "added",
  "type" : "longSum",
  "fieldName" : "added"
},
{
  "name" : "deleted",
  "type" : "longSum",
  "fieldName" : "deleted"
},
{
  "name" : "delta",
  "type" : "longSum",
  "fieldName" : "delta"
},
{
  "name" : "user_unique",
  "type" : "hyperUnique",
  "fieldName" : "user"
}

Một số số liệu hữu ích để tổng hợp liên quan đến tập dữ liệu để tổng số hàng trong tập dữ liệu, count . Một số liệu hữu ích khác để tổng hợp hoặc thu thập tất cả các khóa added , sau đó tính tổng của chúng bằng API LongSum Aggregator của Druid. Chúng ta có thể tìm tổng cho các bản ghi deleted , tương tư sẽ có tổng hợp các bản ghi delta . Một số liệu khác có thể thu thập là user vì mỗi hàng có trường user duy nhất của riêng họ. Tại thời điểm time, các khóa người dùng duy nhất sẽ được tổng hợp thành bộ số liệu hyperUnique.

Bây giờ chúng tôi đã phân tích dữ liệu và tách thành các nhóm timestamp , dimensionsmetrics, thông tin này có thể giúp  viết Druid Ingestion Spec là một dạng thức để truy xuất gọi đến druid vói đầu vào là json

Viết Ingestion Spec để chạy một batch

Chúng ta có thể lấy file ví dụ từ link này:

wget https://raw.githubusercontent.com/hortonworks/data-tutorials/73dfff5c49d732c692d135cb6b572f6ec2783f73/tutorials/hdp/getting-started-with-druid/assets/druid-spec/wikiticker-index.json

hoặc trong thưc mục HDB

/usr/hdp/3.0.1.0-187/druid/quickstart/wikiticker-index.json

hoặc có thể copy nội dung của nó

{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        "dataSource" : "wikipedia",
        "parser" : {
          "type" : "hadoopyString",
          "parseSpec" : {
            "format" : "json",
            "timestampSpec" : {
              "format" : "auto",
              "column" : "time"
            },            
            "dimensionsSpec" : {
              "dimensions" : [
                "channel",
                "cityName",
                "comment",
                "countryIsoCode",
                "countryName",
                "isAnonymous",
                "isMinor",
                "isNew",
                "isRobot",
                "isUnpatrolled",
                "metroCode",
                "namespace",
                "page",
                "regionIsoCode",
                "regionName",
                "user"
              ]
            }
          }
        },
        "metricsSpec" : [
          {
            "name" : "count",
            "type" : "count"
          },
          {
            "name" : "added",
            "type" : "longSum",
            "fieldName" : "added"
          },
          {
            "name" : "deleted",
            "type" : "longSum",
            "fieldName" : "deleted"
          },
          {
            "name" : "delta",
            "type" : "longSum",
            "fieldName" : "delta"
          },
          {
            "name" : "user_unique",
            "type" : "hyperUnique",
            "fieldName" : "user"
          }
        ],
        "granularitySpec" : {
          "type" : "uniform",
          "segmentGranularity" : "day",
          "queryGranularity" : "none",
          "intervals" : ["2015-09-12/2015-09-13"]
        }        
      },
      "ioConfig" : {
        "type" : "hadoop",
        "inputSpec" : {
          "type" : "static",
          "paths" : "quickstart/wikiticker-2015-09-12-sampled.json.gz"
        }
      },      
      "tuningConfig" : {
        "type" : "hadoop",
        "partitionsSpec" : {
          "type" : "hashed",
          "targetPartitionSize" : 5000000
        },
        "jobProperties" : {}
      }
    }
}

Chạy task với Ingestion Spec vừa viết

Phải đảm bảo rằng task đánh index có thể đọc dữ liệu wikiticker-2015-09-12-sampling.json.gz trên HDFS.

Vì mình đã cài đặt Druid trên HDP, nó được kết nối với Hadoop, vì vậy chúng tôi có thể tải wikiticker-2015-09-12-sampling.json.gz lên HDFS.

1. Hãy tạo thư mục HDFS sau:

su druid
hdfs dfs -mkdir -p /user/druid/quickstart

2. Hãy tải tệp dữ liệu json lên HDFS:

hdfs dfs -put /usr/hdp/3.0.1.0-187/druid/quickstart/wikiticker-2015-09-12-sampled.json.gz /user/druid/quickstart/
hdfs dfs -chmod -R 777 /user/druid
exit

3. Hãy khởi động quá trình lập indec bằng cách gửi yêu cầu POST đến Druid Overlord:

curl -X 'POST' -H 'Content-Type:application/json' -d @/tmp/wikiticker-index.json http://<druid-host>:8090/druid/indexer/v1/task

Mở quá tải Druid tại http://localhost:8090/console.html . Tác vụ sẽ xuất hiện dưới tác vụ đang chạy:

Lưu ý: Sẽ mất khoảng 5 - 15 phút để hoàn thành nhiệm vụ.

Nếu mọi việc suôn sẻ với nhiệm vụ này, thì nó sẽ kết thúc với status SUCCEEDED trong giao diện người dùng Druid Overlord. Truy cập "Task log" để khắc phục sự cố nếu có sự cố xảy ra.

Đi đến Druid Coordinator UI tại http://localhost:8081/#/ và bạn sẽ thấy nguồn dữ liệu wikipedia .

Xin chúc mừng! Bạn đã học cách phân tích tập dữ liệu của mình để phân tách thành các chỉ mục timestamp, dimensionsmetrics, đã viết một Druid Ingestion Spec sử dụng dữ liệu được tìm thấy trong tập dữ liệu, gửi thông số kỹ thuật cho Druid Overlord để chỉ định cách bạn muốn tác vụ chỉ mục dựa trên Hadoop được cấu hình khi nó được chạy và nhập dữ liệu hàng loạt vào kho dữ liệu Druid . Trong hướng dẫn tiếp theo, bạn sẽ học cách tạo các tệp json để truy vấn dữ liệu trong Druid.

Phân tích thông số kỹ thuật

Hadoop-based Batch Ingestion

Các tham số của Hadoop-based Batch Ingestion

type
{
    "type" : "index_hadoop"
    ...
}

type - thiết lập loại ingestion task phải được chạy khi thông số kỹ thuật ingestion được gửi tới Druid Overlord. Trong trường hợp này, vì mình đang sử dụng Hadoop, chọn "index_hadoop". Do đó, một ingestion task  liệu dựa trên hadoop sẽ được chạy.

spec
{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        ...        
      },
      "ioConfig" : {
        ...
      },      
      "tuningConfig" : {
        ...
      }
    }
}

Druid ingestion spec (hadoop-index, index, ... ) bao gồm 3 phần:

  • dataSchema (JSON Object) - xác định các đối tượng trong dữ liệu đến.
  • ioConfig (JSON Object) - xác định vị trí HDFS của dữ liệu nguồn. Trên HDP, tác vụ hadoop-index sẽ lưu trữ dữ liệu trong kho Druid theo mặc định, do đó bạn sẽ không cần bao gồm đích.
  • TuneConfig (JSON Object) - chỉ định cách định cấu hình các tham số nhập.
dataSchema
{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        "dataSource" : "wikipedia",
        "parser" : {
          ...
        },
        "metricsSpec" : [
          ...
        ],
        "granularitySpec" : {
          ...
        }        
      },
      "ioConfig" : {
        ...
      },      
      "tuningConfig" : {
        ...
      }
    }
}

Druid dataSchema bao gồm 4 thành phần nhỏ:

  • dataSource (String) - tên của tệp dữ liệu được nhập và có thể được hiểu là một bảng. Trong trường hợp này, mình đang gọi dataSource là wikiticker
  • parser (JSON Object) - xác định cách phân tích dữ liệu được nhập vào các thành phần cú pháp logic (ví dụ: trình phân tích cú pháp string sẽ phân tích từng hàng trong tệp dữ liệu và tìm danh sách các string được phân tách bằng dấu cách, dấu phẩy, v.v.).
  • metricsSpec - một danh sách các bộ tổng hợp . Công cụ tổng hợp là cách thông tin được thu thập và sau đó được thể hiện dưới dạng tóm tắt. Druid có nhiều bộ tổng hợp để thu thập tất cả các loại dữ liệu từ tệp dữ liệu: chẳng hạn như số hàng, tổng các giá trị dưới dạng số nguyên, min của tất cả các giá trị, v.v.
  • granularitySpec - chỉ định cách tạo các phân đoạn và dữ liệu sẽ được cuộn lên.
"dataSchema" -> "parser"
{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        "dataSource" : "wikipedia",
        "parser" : {
          "type" : "hadoopyString",
          "parseSpec" : {
            ...
          }
        },
        "metricsSpec" : [
          ...
        ],
        "granularitySpec" : {
          ...
        }        
      },
      "ioConfig" : {
        ...
      },      
      "tuningConfig" : {
        ...
      }
    }
}

parser Druid bao gồm 2 trường:

  • type -  parser type để sử dụng, trong đoạn mã trên,  sử dụng parser type hadoopyString cho công việc lập chỉ mục Hadoop.
  • parseSpec - xác định định dạng, timestamp và dimensions của dữ liệu.
"dataSchema" -> "parser" -> "parseSpec"
{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        "dataSource" : "wikipedia",
        "parser" : {
          "type" : "hadoopyString",
          "parseSpec" : {
            "format" : "json",
            "timestampSpec" : {
              ...
            },            
            "dimensionsSpec" : {
              ...
            }
          }
        },
        "metricsSpec" : [
          ...
        ],
        "granularitySpec" : {
          ...
        }        
      },
      "ioConfig" : {
        ...
      },      
      "tuningConfig" : {
        ...
      }
    }
}

Mục đích của parseSpec là xác định định dạng của các hàng đến từ tệp dữ liệu tĩnh. Vì tệp dữ liệu của chúng tôi là json, chúng tôi sử dụng định dạng json. Các parseSpec cũng phục vụ để tìm ra timestamp và dimensions của hàng đến.

Đối với parseSpec của Druid , mình sử dụng 3 trường:

  • format - chỉ định loại định dạng dữ liệu của tệp, mình chọn do wikiticker-2015-09-12-sampling.json.gz là tệp json. Nếu định dạng không được chỉ định, theo mặc định, nó sẽ được đặt thành tsv. LƯU Ý: nếu tệp dữ liệu của bạn ở dạng CSV hoặc TSV và hàng đầu tiên của tệp của bạn không có tiêu đề, thì bạn sẽ cần bao gồm một trường columns trong parseSpec .
  • timestampSpec - xác định cột và định dạng của timestamp.
  • dimensionSpec - định nghĩa kích thước của dữ liệu. Kích thước là thuộc tính chúng ta có thể lọc và phân chia.
  • columns - chỉ cần cho các tệp dữ liệu CSV và TSV. Nó nói với Druid về các cột của dữ liệu.
"dataSchema" -> "parser" -> "parseSpec" -> "timestampSpec"
{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        "dataSource" : "wikipedia",
        "parser" : {
          "type" : "hadoopyString",
          "parseSpec" : {
            "format" : "json",
            "timestampSpec" : {
              "format" : "auto",
              "column" : "time"
            },            
            "dimensionsSpec" : {
              ...
            }
          }
        },
        "metricsSpec" : [
          ...
        ],
        "granularitySpec" : {
          ...
        }        
      },
      "ioConfig" : {
        ...
      },      
      "tuningConfig" : {
        ...
      }
    }
}

Đối với trường timestamp của Druid, mình sử dụng 2 trường:

  • format -. quy định cụ thể định dạng timestamp, mình chọn auto để tự động xác định các timestamp.
  • column - chỉ định cột có thể tìm thấy timestamp. Trong trường hợp này, mình yêu cầu trình phân tích cú pháp nhìn vào cột time để lấy timestamp.
"dataSchema" -> "parser" -> "parseSpec" -> "dimensionSpec"
{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        "dataSource" : "wikipedia",
        "parser" : {
          "type" : "hadoopyString",
          "parseSpec" : {
            "format" : "json",
            "timestampSpec" : {
              "format" : "auto",
              "column" : "time"
            },            
            "dimensionsSpec" : {
              "dimensions" : [
                "channel",
                "cityName",
                "comment",
                "countryIsoCode",
                "countryName",
                "isAnonymous",
                "isMinor",
                "isNew",
                "isRobot",
                "isUnpatrolled",
                "metroCode",
                "namespace",
                "page",
                "regionIsoCode",
                "regionName",
                "user"
              ]
            }
          }
        },
        "metricsSpec" : [
          ...
        ],
        "granularitySpec" : {
          ...
        }        
      },
      "ioConfig" : {
        ...
      },      
      "tuningConfig" : {
        ...
      }
    }
}

Đối với dimensionSpec , mình sử dụng 1 trường:

  • dimensions - một danh sách các đối tượng dimension schema kiểu chuỗi được ký hiệu bằng tên cụ thể của chúng. Nếu đây là một mảng trống, Druid sẽ coi tất cả các cột là các cột là timestamp và number.

Trong trường hợp này, mình đã thêm

"dimensions" : [
  "channel",
  "cityName",
  "comment",
  "countryIsoCode",
  "countryName",
  "isAnonymous",
  "isMinor",
  "isNew",
  "isRobot",
  "isUnpatrolled",
  "metroCode",
  "namespace",
  "page",
  "regionIsoCode",
  "regionName",
  "user"
]

vào trường dimensions vì các đối tượng này được ký hiệu là Chuỗi được nhập trong bộ dữ liệu wikiticker-2015-09-12-sampling.json.

Dimension Schema bào gồm tất các cột kiểu chuỗi

"dataSchema" -> "metricsSpec"
{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        "dataSource" : "wikipedia",
        "parser" : {
          "type" : "hadoopyString",
          "parseSpec" : {
            "format" : "json",
            "timestampSpec" : {
              "format" : "auto",
              "column" : "time"
            },            
            "dimensionsSpec" : {
              "dimensions" : [
                "channel",
                "cityName",
                "comment",
                "countryIsoCode",
                "countryName",
                "isAnonymous",
                "isMinor",
                "isNew",
                "isRobot",
                "isUnpatrolled",
                "metroCode",
                "namespace",
                "page",
                "regionIsoCode",
                "regionName",
                "user"
              ]
            }
          }
        },
        "metricsSpec" : [
          {
            "name" : "count",
            "type" : "count"
          },
          {
            "name" : "added",
            "type" : "longSum",
            "fieldName" : "added"
          },
          {
            "name" : "deleted",
            "type" : "longSum",
            "fieldName" : "deleted"
          },
          {
            "name" : "delta",
            "type" : "longSum",
            "fieldName" : "delta"
          },
          {
            "name" : "user_unique",
            "type" : "hyperUnique",
            "fieldName" : "user"
          }
        ],
        "granularitySpec" : {
          ...
        }        
      },
      "ioConfig" : {
        ...
      },      
      "tuningConfig" : {
        ...
      }
    }
}

Đối với Druid metricsSpec , chúng tôi sử dụng 3 aggregators:

  • Count Aggregator  - đếm số lượng dữ liệu đã nhập
{
    "_comment" : "definition of count aggregator"
    {
        "type" : "count",
        "name" : <output_name>
    }
}
  • longSum Aggregator - tính tổng các giá trị dưới dạng số nguyên có size 64 bit. name là tên đầu ra cho kết quả tổng hợp và fieldName là tên của cột số liệu để tổng hợp
    "_comment" : "definition of longSum aggregator"
    {
        "type" : "longSum",
        "name" : <output_name>,
        "fieldName" : <metric_name>
    }

Trong trường hợp này, mình muốn tính tổng của tất cả các giá trị cột added từ bộ dữ liệu wikiticker-2015-09-12-sampling.json và sau đó lưu kết quả vào tên đầu ra added.

    "_comment" : "from our json file, longSum aggregator"
    {
        "name" : "added",
        "type" : "longSum",
        "fieldName" : "added"
    }

Tương tự có thể được áp dụng cho cột deleteddelta trong đó tất cả các giá trị đã xóa trong tập dữ liệu của chúng tôi được tính cho tổng, sau đó được lưu trữ vào tên đầu ra deleteddelta .

  • HyperUnique Aggregator - tính toán số lượng phần tử ước tính đã được thêm vào một tập hợp được thu thập tại số liệu "hyperUnique" tại thời điểm lập chỉ mục.
    "_comment" : "definition of hyperUnique aggregator"
    {
        "type" : "hyperUnique",
        "name" : <output_name>,
        "fieldName" : <metric_name>
    }

Trong trường hợp user, mình đang ước tính số lượng user duy nhất trong bộ dữ liệu wikiticker trong mỗi khoảng thời gian mỗi ngày, sau đó lưu trữ nó vào tên đầu ra user_unique .

    "_comment" : "from our json file, hyperUnique aggregator"
    {
        "name" : "user_unique",
        "type" : "hyperUnique",
        "fieldName" : "user"
    }
"dataSchema" -> "granularitySpec"
{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        "dataSource" : "wikipedia",
        "parser" : {
          "type" : "hadoopyString",
          "parseSpec" : {
            "format" : "json",
            "timestampSpec" : {
              "format" : "auto",
              "column" : "time"
            },            
            "dimensionsSpec" : {
              "dimensions" : [
                "channel",
                "cityName",
                "comment",
                "countryIsoCode",
                "countryName",
                "isAnonymous",
                "isMinor",
                "isNew",
                "isRobot",
                "isUnpatrolled",
                "metroCode",
                "namespace",
                "page",
                "regionIsoCode",
                "regionName",
                "user"
              ]
            }
          }
        },
        "metricsSpec" : [
          {
            "name" : "count",
            "type" : "count"
          },
          {
            "name" : "added",
            "type" : "longSum",
            "fieldName" : "added"
          },
          {
            "name" : "deleted",
            "type" : "longSum",
            "fieldName" : "deleted"
          },
          {
            "name" : "delta",
            "type" : "longSum",
            "fieldName" : "delta"
          },
          {
            "name" : "user_unique",
            "type" : "hyperUnique",
            "fieldName" : "user"
          }
        ],
        "granularitySpec" : {
          "type" : "uniform",
          "segmentGranularity" : "day",
          "queryGranularity" : "none",
          "intervals" : ["2015-09-12/2015-09-13"]
        }        
      },
      "ioConfig" : {
        ...
      },      
      "tuningConfig" : {
        ...
      }
    }
}

Đối với granularitySpec, có 4 trường:

  • type - chỉ định loại phân đoạn khoảng sẽ được tạo. mình đặt nó là uniform.
  • segmentGranularity - chỉ định mức độ chi tiết để tạo các phân đoạn. Mình chỉ định các phân đoạn sẽ được tạo ra mỗi ngày.
"segmentGranularity" : "day"
  • queryGranularity - độ chi tiết tối thiểu có khả năng truy vấn kết quả và độ chi tiết của dữ liệu trong phân khúc. Hiện tại, mình đã chỉ định rằng sẽ không có độ chi tiết tối thiểu để truy vấn kết quả.
"queryGranularity" : "none"
  • intervals - chỉ định khoảng thời gian cho dữ liệu thô được nhập. "2015-09-12 / 2015-09-13" , mình định dữ liệu sẽ được nhập vào chỉ trong khoảng thời gian 1 ngày. Nếu thay đổi khoảng thời gian, có thể nhập dữ liệu trong khoảng thời gian 30 ngày, v.v.
"intervals" : ["2015-09-12/2015-09-13"]
"ioConfig"
{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        "dataSource" : "wikipedia",
        "parser" : {
          "type" : "hadoopyString",
          "parseSpec" : {
            "format" : "json",
            "timestampSpec" : {
              "format" : "auto",
              "column" : "time"
            },            
            "dimensionsSpec" : {
              "dimensions" : [
                "channel",
                "cityName",
                "comment",
                "countryIsoCode",
                "countryName",
                "isAnonymous",
                "isMinor",
                "isNew",
                "isRobot",
                "isUnpatrolled",
                "metroCode",
                "namespace",
                "page",
                "regionIsoCode",
                "regionName",
                "user"
              ]
            }
          }
        },
        "metricsSpec" : [
          {
            "name" : "count",
            "type" : "count"
          },
          {
            "name" : "added",
            "type" : "longSum",
            "fieldName" : "added"
          },
          {
            "name" : "deleted",
            "type" : "longSum",
            "fieldName" : "deleted"
          },
          {
            "name" : "delta",
            "type" : "longSum",
            "fieldName" : "delta"
          },
          {
            "name" : "user_unique",
            "type" : "hyperUnique",
            "fieldName" : "user"
          }
        ],
        "granularitySpec" : {
          "type" : "uniform",
          "segmentGranularity" : "day",
          "queryGranularity" : "none",
          "intervals" : ["2015-09-12/2015-09-13"]
        }        
      },
      "ioConfig" : {
        "type" : "hadoop",
        "inputSpec" : {
          ...
        }
      },      
      "tuningConfig" : {
        ...
      }
    }
}

Đối với ioConfig , có 2 trường:

  • type - luôn luôn nên có. Mình đang sử dụng dựa trên hadoop.
  • inputSpec - chứa vị trí của HDFS để lấy dữ liệu tĩnh từ đó.
"ioConfig" -> "inputSpec"
{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        "dataSource" : "wikipedia",
        "parser" : {
          "type" : "hadoopyString",
          "parseSpec" : {
            "format" : "json",
            "timestampSpec" : {
              "format" : "auto",
              "column" : "time"
            },            
            "dimensionsSpec" : {
              "dimensions" : [
                "channel",
                "cityName",
                "comment",
                "countryIsoCode",
                "countryName",
                "isAnonymous",
                "isMinor",
                "isNew",
                "isRobot",
                "isUnpatrolled",
                "metroCode",
                "namespace",
                "page",
                "regionIsoCode",
                "regionName",
                "user"
              ]
            }
          }
        },
        "metricsSpec" : [
          {
            "name" : "count",
            "type" : "count"
          },
          {
            "name" : "added",
            "type" : "longSum",
            "fieldName" : "added"
          },
          {
            "name" : "deleted",
            "type" : "longSum",
            "fieldName" : "deleted"
          },
          {
            "name" : "delta",
            "type" : "longSum",
            "fieldName" : "delta"
          },
          {
            "name" : "user_unique",
            "type" : "hyperUnique",
            "fieldName" : "user"
          }
        ],
        "granularitySpec" : {
          "type" : "uniform",
          "segmentGranularity" : "day",
          "queryGranularity" : "none",
          "intervals" : ["2015-09-12/2015-09-13"]
        }        
      },
      "ioConfig" : {
        "type" : "hadoop",
        "inputSpec" : {
          "type" : "static",
          "paths" : "quickstart/wikiticker-2015-09-12-sampled.json.gz"
        }
      },      
      "tuningConfig" : {
        ...
      }
    }
}

Đối với inputSpec, sử dụng 2 trường:

  • type - biểu thị loại inputSpec trong đó đường dẫn tĩnh đến tệp dữ liệu được cung cấp.
"type" : "static"
  • paths - là các đường dẫn đầu vào chỉ ra nơi trong HDFS có thể tìm thấy dữ liệu thô
"paths" : "quickstart/wikiticker-2015-09-12-sampled.json.gz"

Nếu đường dẫn được chỉ định cho dữ liệu thô không tồn tại trong HDFS, thì sẽ gặp lỗi khi gửi tác vụ, Druid overlord sẽ trả về tác vụ không được gửi.

"tunConfig"
{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        "dataSource" : "wikipedia",
        "parser" : {
          "type" : "hadoopyString",
          "parseSpec" : {
            "format" : "json",
            "timestampSpec" : {
              "format" : "auto",
              "column" : "time"
            },            
            "dimensionsSpec" : {
              "dimensions" : [
                "channel",
                "cityName",
                "comment",
                "countryIsoCode",
                "countryName",
                "isAnonymous",
                "isMinor",
                "isNew",
                "isRobot",
                "isUnpatrolled",
                "metroCode",
                "namespace",
                "page",
                "regionIsoCode",
                "regionName",
                "user"
              ]
            }
          }
        },
        "metricsSpec" : [
          {
            "name" : "count",
            "type" : "count"
          },
          {
            "name" : "added",
            "type" : "longSum",
            "fieldName" : "added"
          },
          {
            "name" : "deleted",
            "type" : "longSum",
            "fieldName" : "deleted"
          },
          {
            "name" : "delta",
            "type" : "longSum",
            "fieldName" : "delta"
          },
          {
            "name" : "user_unique",
            "type" : "hyperUnique",
            "fieldName" : "user"
          }
        ],
        "granularitySpec" : {
          "type" : "uniform",
          "segmentGranularity" : "day",
          "queryGranularity" : "none",
          "intervals" : ["2015-09-12/2015-09-13"]
        }        
      },
      "ioConfig" : {
        "type" : "hadoop",
        "inputSpec" : {
          "type" : "static",
          "paths" : "quickstart/wikiticker-2015-09-12-sampled.json.gz"
        }
      },      
      "tuningConfig" : {
        "type" : "hadoop",
        "partitionsSpec" : {
          ...
        },
        "jobProperties" : {
          ...  
        }
      }
    }
}

tuningConfig có 3 trường:

  • type - loại môi trường trong đó điều chỉnh thông số kỹ thuật nhập sẽ diễn ra. Sử dụng hadoop để nhập dữ liệu vào Druid
  • partitionsSpec - chỉ định cách phân vùng mỗi nhóm thành các phân đoạn. Nếu thuộc tính này không được bao gồm, không có phân vùng sẽ xảy ra.
  • jobProperties - một danh sách các thuộc tính để kết hợp vào cấu hình công việc Hadoop
"tuningConfig" -> "partitionsSpec"
{
    "type" : "index_hadoop",
    "spec" : {
      "dataSchema" : {
        "dataSource" : "wikipedia",
        "parser" : {
          "type" : "hadoopyString",
          "parseSpec" : {
            "format" : "json",
            "timestampSpec" : {
              "format" : "auto",
              "column" : "time"
            },            
            "dimensionsSpec" : {
              "dimensions" : [
                "channel",
                "cityName",
                "comment",
                "countryIsoCode",
                "countryName",
                "isAnonymous",
                "isMinor",
                "isNew",
                "isRobot",
                "isUnpatrolled",
                "metroCode",
                "namespace",
                "page",
                "regionIsoCode",
                "regionName",
                "user"
              ]
            }
          }
        },
        "metricsSpec" : [
          {
            "name" : "count",
            "type" : "count"
          },
          {
            "name" : "added",
            "type" : "longSum",
            "fieldName" : "added"
          },
          {
            "name" : "deleted",
            "type" : "longSum",
            "fieldName" : "deleted"
          },
          {
            "name" : "delta",
            "type" : "longSum",
            "fieldName" : "delta"
          },
          {
            "name" : "user_unique",
            "type" : "hyperUnique",
            "fieldName" : "user"
          }
        ],
        "granularitySpec" : {
          "type" : "uniform",
          "segmentGranularity" : "day",
          "queryGranularity" : "none",
          "intervals" : ["2015-09-12/2015-09-13"]
        }        
      },
      "ioConfig" : {
        "type" : "hadoop",
        "inputSpec" : {
          "type" : "static",
          "paths" : "quickstart/wikiticker-2015-09-12-sampled.json.gz"
        }
      },      
      "tuningConfig" : {
        "type" : "hadoop",
        "partitionsSpec" : {
          "type" : "hashed",
          "targetPartitionSize" : 5000000
        },
        "jobProperties" : {}
      }
    }
}

Các phân đoạn(segments) được phân vùng như thế nào?

Các phân đoạn được phân vùng trên timestamp. Druid cung cấp hỗ trợ cho hai loại phân vùng: hash và dimension. Hash được dựa trên hàm băm của tất cả các dimension trong mỗi hàng trong khi dimension phải thực hiện với các phạm vi của một dimension duy nhất.

Chúng tôi sử dụng phân vùng Hash. Một lợi thế bằng cách sử dụng phương pháp này là nó cải thiện hiệu suất lập chỉ mục và nó tạo ra các phân đoạn dữ liệu có kích thước đồng đều hơn.

Đối với partitionsSpec , chúng tôi sử dụng 2 trường:

  • type - kiểu phân vùng sẽ được sử dụng. Mình sử dụng phân vùng hash . Phân vùng băm có nghĩa là trước tiên số lượng phân đoạn sẽ được chọn, sau đó các hàng sẽ được phân vùng trên các phân đoạn đó dựa trên hàm băm của tất cả các kích thước trong mỗi hàng.
  • targetPartitionSize - là số lượng hàng được bao gồm trong một phân vùng. Chúng tôi đã sử dụng 5000000 byte, tức là 5 MB.

Tổng kết

Trong phần tới mình sẽ trình bầy  cách viết các truy vấn dựa trên JSON của Druid. Và sử dụng Zeppelin để viết các truy vấn Druid và chạy chúng dựa trên nguồn dữ liệu wikipedia.

Tài liệu tham khảo