使用LogstashおよびJDBCはペースを維持するために、リレーショナルデータベースとElasticsearchを確保します

強力な検索機能Elasticsearchプランを最大限に活用するためには、多くの企業は、既存のリレーショナルデータベースに基づいてElasticsearchを展開します。この場合には、リレーショナルデータベースElasticsearch関連同期してそのデータを確保する必要があるかもしれません。そこで、この記事では、私が効率的にElasticsearchに同期して更新されたデータとリレーショナルデータベースを複製するLogstashを使用する方法を示します。コード、本明細書に記載された方法は、MySQLを使用してテストされているが、理論的にはすべてのリレーショナルデータベース管理システム(RDBMS)に適用する必要があります。

システム構成

この記事では、以下の製品がテストされた使用します。

全体の同期ステップの概要

この記事では、我々は、MySQLとElasticsearchのキープペースを作るためにLogstashとJDBC入力プラグインを使用します。概念的には、JDBCプラグインのLogstash入力は、レコードを挿入したり、ループの最後の繰り返し以降に変更されて見つけるために定期的にMySQLの世論調査にループを実行します。それが適切に実行するためには、次の条件を満たす必要があります。

  1. 文書はMySQLのElasticsearch、Elasticsearchで書かれている場合は、「_id」フィールドには、MySQLの「ID」フィールドに設定する必要があります。これは、MySQLとの間に直接のマッピングがElasticsearchドキュメントを記録確立することができます。あなたがMySQLでレコードを更新した場合、それはElasticsearch内の関連レコードの全体をカバーします。文書はElasticsearch効率とでカバーますのでご注意ください更新操作の内側から話すの原則は、更新がその後インデックスを付け、古い文書や新しいドキュメントの削除が含まれるため、効率的なように。
  2. データを挿入またはレコードは、時間の更新または挿入フィールドを含まなければならないのMySQLに更新された場合。このフィールドは、あなただけの最後のポーリングループ反復の編集や挿入書類の後に許可Logstashを要求することができます。ときにMySQLのLogstashの世論調査のそれぞれは、時間を読んだり、MySQLのから挿入した最後のレコードを更新するために保存されます。Updateまたは受信ポーリングループの繰り返しで、後に最後のレコードより挿入します。次の繰り返しで、Logstashは、我々は以下の条件を満たしたレコードを取得するための唯一の要求であることを知っています。

上記の条件が満たされている場合、我々は定期的にMySQLのからすべてのレコードの新しいまたは編集を要求するために、Logstashを構成し、Elasticsearchに書き込むことができます。Logstashコードこれらの操作の完了は、この記事の後半で表示されます。

MySQLの設定

次のコードの設定MySQLデータベースとテーブルを使用することができます。

CREATE DATABASE es_db;
USE es_db;
DROP TABLE IF EXISTS es_table;
CREATE TABLE es_table (
  id BIGINT(20) UNSIGNED NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY unique_id (id),
  client_name VARCHAR(32) NOT NULL,
  modification_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  insertion_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

上記の構成では、MySQLは、いくつかのパラメータは、特別な注意が必要があります。

  • es_table:これは、MySQLのデータテーブルの名前で、データはここから読み出され、Elasticsearchに同期されます。
  • id:これは記録一意の識別子です。"idが" PRIMARY KEY(主キー)とUNIQUE KEY(ユニークキー)として定義されていることに注意してください。各「ID」は、現在のテーブルに一度だけ表示されていることをこれが保証されます。これは、文書にし、中ElasticsearchにElasticsearchにドキュメントを更新するため、「_id」に変換されます。
  • client_name:このフィールドは、各レコードのユーザ定義データが格納されていることを示します。この記事では、簡単にするために、我々は、ユーザー定義のデータが含まれている一つのフィールドを持っていますが、簡単に複数のフィールドを追加することができます。私たちは、新しく挿入されたレコードがでMySQLのElasticsearchにコピーされていないだけで、あなたに証明し、またElasticsearchの右側に広がってレコードを更新するためには、このフィールドで変更します。
  • modification_time:あなたはどのMySQLのレコードを挿入または変更すると、これは時間の編集を定義するために、フィールドの値になります。この編集時間で、我々は最後のLogstashは、MySQLから編集後のレコードを要求するので、任意の記録を抽出することができるようになります。
  • insertion_time:このフィールドは、主にデモの目的のために使用され、厳密には必要な条件が正しく同期満たされます。我々は最初のMySQLに挿入された時間を追跡するためにそれを使用します。

MySQLの操作

以上のように構成した後、記録は次の文でのMySQLに書き込むことができます。

INSERT INTO es_table (id, client_name) VALUES (<id>, <client name>);

次のコマンドでMySQLのレコードを更新することができます。

UPDATE es_table SET client_name = <new client name> WHERE id=<id>;

次の文MySQLの更新/挿入(アップサート)によって達成することができます。

INSERT INTO es_table (id, client_name) VALUES (<id>, <client name when created> ON DUPLICATE KEY UPDATE client_name=<client name when updated>;

同期コード

以下の実施形態は、前のセクションで説明したパイプライン同期コードをLogstashます。

input {
  jdbc {
    jdbc_driver_library => "<path>/mysql-connector-java-8.0.16.jar"
    jdbc_driver_class => "com.mysql.jdbc.Driver"
    jdbc_connection_string => "jdbc:mysql://<MySQL host>:3306/es_db"
    jdbc_user => <my username>
    jdbc_password => <my password>
    jdbc_paging_enabled => true
    tracking_column => "unix_ts_in_secs"
    use_column_value => true
    tracking_column_type => "numeric"
    schedule => "*/5 * * * * *"
    statement => "SELECT *, UNIX_TIMESTAMP(modification_time) AS unix_ts_in_secs FROM es_table WHERE (UNIX_TIMESTAMP(modification_time) > :sql_last_value AND modification_time < NOW()) ORDER BY modification_time ASC"
  }
}
filter {
  mutate {
    copy => { "id" => "[@metadata][_id]"}
    remove_field => ["id", "@version", "unix_ts_in_secs"]
  }
}
output {
  # stdout { codec =>  "rubydebug"}
  elasticsearch {
      index => "rdbms_sync_idx"
      document_id => "%{[@metadata][_id]}"
  }
}
Read Less

パイプラインでは、我々はいくつかの分野に焦点を当てることができます:

  • tracking_column:このフィールドは、に格納されている「unix_ts_in_secs」(以下に説明するようにLogstashには、MySQLから読み出さ最後文書を追跡するため)フィールド、指定.logstash_jdbc_last_runディスク上を。この値は、要求されたドキュメントのそのポーリングのループ反復で開始値のLogstashを決定するために使用されます。SELECT文の「sql_last_value」によるアクセス:.logstash_jdbc_last_run値のように保存されています。
  • unix_ts_in_secs:これは標準であって、フィールドによって生成されたSELECT文ですUnixの時間「MODIFICATION_TIME」(以降エポックからの秒数)。私達はちょうど議論し、「トラッキング欄には、」フィールドを参照します。進捗状況を追跡するための、というよりも、単純なタイムスタンプなどのUnixタイムスタンプ、UMTとローカルタイムゾーンの間で前後に切り替えるには、右は非常に複雑なプロセスであるので、タイムスタンプなどの単純なようで、エラーが発生することがあります。
  • sql_last_value:これは内蔵のパラメータポイントLogstashポーリン現在の反復を開始含め、JDBC構成上のSELECT文を入力し、このパラメータを参照します。このフィールドは、の(.logstash_jdbc_last_runから読み取る)最新値「unix_ts_in_secs」に設定されます。実行Logstash・ポーリング・ループでMySQLのクエリは、それが文書の返還のための開始点として使用されます。再びElasticsearchに挿入または更新内容の前にElasticsearchに伝播しないことを確実にするために、クエリでこの変数を追加します。
  • schedule:その使用のcron構文は変更を見つけるために、MySQLのポーリングをLogstashする頻度を指定します。ここで指定"*/5 * * * * *"Logstashごとに5秒の接触時間のMySQLを教えてくれます。
  • modification_time < NOW():SELECTのこの部分は、説明するのは難しい概念であり、我々は次のセクションで詳しく説明します。
  • filter:このセクションでは、我々は単にMySQLのレコードの「ID」の値は、我々が書き込みElasticsearchの各いることを確実にするために、出力の後にフィールドを参照しますので、メタデータフィールドを「_id」という名前のファイルにコピーされます正しい文書「_id」の値を持っています。メタデータフィールドを使用すると、これは新しいフィールドを作成するために、一時的な値につながらないことを確実にすることができます。我々はこれらの分野でElasticsearchへの書き込みにはしたくないので、私たちはまた、「ID」、「@バージョン」と文書から「unix_ts_in_secs」フィールドを削除しました。
  • output:このセクションでは、各文書がElasticsearch、割り当てる必要が書かれるべきであることを指定します_idコメントアウトされたコードがあるでしょう(私たちは、スクリーニングのセクションで作成したメタデータフィールドから抽出するためには)、この出力はあなたがトラブルシューティングに役立つことができた後rubydebug出力が有効になっているが含まれています。

SELECT文の妥当性分析

このセクションでは、SELECT文の中で理由を説明するために詳細を追加するmodification_time < NOW()非常に重要です。この概念を説明するためには、まずあなたの仕事になぜ二つの最も直感的な方法を示すために、いくつかの負の例を与えます。追加する理由を次に説明modification_time < NOW()直感的な方法によって引き起こされる二つの問題を克服することができ。

この方法で直感的なアプリケーション:A

このセクションでは、我々はWHERE句が含まれていない場合を説明しますmodification_time < NOW()が、唯一の指定されたUNIX_TIMESTAMP(modification_time) > :sql_last_value場合、何が起こります。この場合、以下のように、SELECT文は次のとおりです。

statement => "SELECT *, UNIX_TIMESTAMP(modification_time) AS unix_ts_in_secs FROM es_table WHERE (UNIX_TIMESTAMP(modification_time) > :sql_last_value) ORDER BY modification_time ASC"

一見すると、上記の方法は、通常のように実行しなければならないが、いくつかのエッジケースのために、それはいくつかの文書を逃すことがあります。たとえば、私たちは、MySQLは現在、毎秒二つの文書を挿入し、Logstash SELECT文は5秒ごとに一度実行されていることを前提としています。T10にT0以下に示す詳細は、それぞれ毎秒を代表するものであり、それは、MySQLのデータがR22にR1を表現置きます。我々は、ブロックシアンに示すようにR11にR1、原稿を読み取り、第Logstashポーリングループ反復がT5を発生すると仮定します。sql_last_valueこれが最後のレコード(R11)のタイムスタンプを読んであるため、値が今、T5に格納されます。また、Logstashは、MySQLからファイルを読み終えた後、T5文書R12のための別のタイムスタンプは、すぐにMySQLのに挿入されていることを前提としています。

SELECT文の上記の次の反復では、我々は意志だけ抽出時間は(と後でT5の文書よりもWHERE (UNIX_TIMESTAMP(modification_time) > :sql_last_value)レコードがR12をスキップされる手段は、指定された場合です)。あなたが青緑色のボックスは、レコードLogstashが現在の反復で読む表す以下のチャートを見ることができる、灰色のボックスがLogstash前にお読みレコードを表します。

あなたは、この場合に、SELECT文を使用する場合、レコードはElasticsearchでR12に書き込まれないことに注意してください。

この方法で直感的なアプリケーション:2

上記の問題を解決するには、句がより大きいか等しい(以上またはそれ以降に等しい)WHERE、として以下の変更を決定することがあります。

statement => "SELECT *, UNIX_TIMESTAMP(modification_time) AS unix_ts_in_secs FROM es_table WHERE (UNIX_TIMESTAMP(modification_time) >= :sql_last_value) ORDER BY modification_time ASC"

しかし、この実装戦略は理想的ではありません。この場合の問題点は次のとおりです。最新の時間間隔でのMySQLからの最近の文書の読み取りを繰り返しElasticsearchに送信されます。これは、結果の正確性に影響を及ぼさないだろうが、それは有用な作業を行うしていますが。そして、最初の部分のような、イラストショーのMySQLから読み出されたドキュメント以下の初期反復Logstashの世論調査、後。

その後のLogstashポーリング反復を実行するとき、我々はより後またはT5の文書に等しいがすべて抽出されます。以下のチャートで見つけることができます。注:(紫色で示した)のレコード11を再びElasticsearchに送信されます。

最初の2つのケースがその良いものではありません。前者の場合、データの損失、第二の場合には、MySQLから冗長データを読み出してElasticsearchにデータを送信している間に。

直感的な方法によって引き起こされる問題を解決する方法

最初の2例は理想的ではないが与えられ、別のアプローチが採用されなければなりません。指定することで(UNIX_TIMESTAMP(modification_time) > :sql_last_value AND modification_time < NOW())、我々はElasticsearchに各ドキュメントを送信し、一度だけ送信されます。

请参见下面的图表,其中当前的 Logstash 轮询会在 T5 执行。请注意,由于必须满足 modification_time < NOW(),所以只会从 MySQL 中读取截至(但不包括)时间段 T5 的文档。由于我们已经提取了 T4 的全部文档,而未读取 T5 的任何文档,所以我们知道对于下一次的Logstash 轮询迭代,sql_last_value 将会被设置为 T4。

下图演示了在 Logstash 轮询的下一次迭代中将会发生什么情况。由于 UNIX_TIMESTAMP(modification_time) > :sql_last_value,并且 sql_last_value 设置为 T4,我们知道仅会从 T5 开始提取文档。此外,由于只会提取满足 modification_time < NOW() 的文档,所以仅会提取到截至(含)T9 的文档。再说一遍,这意味着 T9 中的所有文档都已提取出来,而且对于下一次迭代 sql_last_value 将会设置为 T9。所以这一方法消除了对于任何给定时间间隔仅检索到 MySQL 文档的一个子集的风险。

测试系统

可以通过一些简单测试来展示我们的实施方案能够实现预期效果。我们可以使用下列命令向 MySQL 中写入记录:

INSERT INTO es_table (id, client_name) VALUES (1, 'Jim Carrey');
INSERT INTO es_table (id, client_name) VALUES (2, 'Mike Myers');
INSERT INTO es_table (id, client_name) VALUES (3, 'Bryan Adams');

JDBC 输入计划触发了从 MySQL 读取记录的操作并将记录写入 Elasticsearch 后,我们即可运行下列 Elasticsearch 查询来查看 Elasticsearch 中的文档:

GET rdbms_sync_idx/_search

其会返回类似下面回复的内容:

"hits" : {
    "total" : {
      "value" :3,
      "relation" : "eq"
    },
    "max_score" :1.0,
    "hits" : [
      {
        "_index" : "rdbms_sync_idx",
        "_type" : "_doc",
        "_id" :"1",
        "_score" :1.0,
        "_source" : {
          "insertion_time" :"2019-06-18T12:58:56.000Z",
          "@timestamp" :"2019-06-18T13:04:27.436Z",
          "modification_time" :"2019-06-18T12:58:56.000Z",
          "client_name" :"Jim Carrey"
        }
      },
Etc …

然后我们可以使用下列命令更新在 MySQL 中对应至 _id=1 的文档:

UPDATE es_table SET client_name = 'Jimbo Kerry' WHERE id=1;

其会正确更新 _id 被识别为 1 的文档。我们可以通过运行下列命令直接查看 Elasticsearch 中的文档:

GET rdbms_sync_idx/_doc/1

其会返回一个类似下面的文档:

{
  "_index" : "rdbms_sync_idx",
  "_type" : "_doc",
  "_id" :"1",
  "_version" :2,
  "_seq_no" :3,
  "_primary_term" :1,
  "found" : true,
  "_source" : {
    "insertion_time" :"2019-06-18T12:58:56.000Z",
    "@timestamp" :"2019-06-18T13:09:30.300Z",
    "modification_time" :"2019-06-18T13:09:28.000Z",
    "client_name" :"Jimbo Kerry"
  }
}

请注意 _version 现已设置为 2,modification_time 现在已不同于 insertion_time,并且 client_name 字段已正确更新至新值。在本例中,@timestamp 字段的用处并不大,由 Logstash 默认添加。

MySQL 中的更新/插入 (upsert) 可通过下列命令完成,您可以验证正确信息是否会反映在 Elasticsearch 中:

INSERT INTO es_table (id, client_name) VALUES (4, 'Bob is new') ON DUPLICATE KEY UPDATE client_name='Bob exists already';

那么删除文档呢

聪明的读者可能已经发现,如果从 MySQL 中删除一个文档,那么这一删除操作并不会传播到 Elasticsearch。可以考虑通过下列方法来解决这一问题:

  1. MySQL 记录可以包含一个 "is_deleted" 字段,用来显示该条记录是否仍有效。这一方法被称为“软删除”。正如对 MySQL 中的记录进行其他更新一样,"is_deleted" 字段将会通过 Logstash 传播至 Elasticsearch。如果实施这一方法,则需要编写 Elasticsearch 和 MySQL 查询,从而将 "is_deleted" 为 “true”(正)的记录/文档排除在外。 最后,可以通过后台作业来从 MySQL 和 Elastic 中移除此类文档。
  2. 另一种方法是确保负责从 MySQL 中删除记录的任何系统随后也会执行一条命令,从而直接从 Elasticsearch 中删除相应文档。

おすすめ

転載: www.cnblogs.com/sanduzxcvbnm/p/12076516.html