MySQLデータベース監視binlogを有効にする手順

MySQLデータベース監視binlogを有効にする手順

序文

多くの場合、ユーザーが自分のデータに対して実行する操作に基づいて何かを行う必要があります。

たとえば、ユーザーがアカウントを削除した場合、私たちはそのユーザーを叱責し、再度アカウントに戻ってきてもらうよう懇願するテキストメッセージを送信します。

同様の機能はビジネス ロジック層に実装でき、ユーザーの削除要求を受け取った後にこの操作を実行できますが、データベース binlog は別の操作方法を提供します。

binlog を監視するには、2 つの手順が必要です。最初の手順は、もちろん MySQL でこの機能を有効にすることです。2 番目の手順は、ログを読み取るプログラムを作成することです。

mysql は binlog をオンにします。

まず、MySQL の binlog は通常は開かれていないため、次のものが必要です。

mysql 構成ファイル my.cnf を見つけます。場所はオペレーティング システムによって異なる場合があります。自分で見つけることができます。

これに次の内容を追加します。

[mysqld]
サーバーID = 1
ログ bin = mysql bin
binlog 形式 = ROW

次に、mysql を再起動します。

/ウブントゥ
サービスmysqlの再起動
// マック
mysql.server の再起動

正常に有効化されているかどうかを監視する

mysql コマンドラインを入力して実行します:

'%log_bin%' のような変数を表示します。

結果が以下の通りであれば成功です。


書き込まれているバイナリログのステータスを表示します。


binlog を読み取るコード

依存関係の導入

私たちはオープンソースの実装をいくつか使用しています。奇妙な理由から、mysql-binlog-connector-java パッケージ (公式 github リポジトリ) [gi​​thub.com/shyiko/mysq…] を選択しました。具体的な依存関係は次のとおりです。

<!-- https://mvnrepository.com/artifact/com.github.shyiko/mysql-binlog-connector-java -->
 <依存関係>
 <groupId>com.github.shyiko</groupId>
 <artifactId>mysql-binlog-connector-java</artifactId>
 <バージョン>0.17.0</バージョン>
 </依存関係>

もちろん、binlog 処理用のオープンソース実装は多数あり、Alibaba の cancl もその 1 つであり、これを使用することもできます。

デモを書く

公式リポジトリの readme に従って、簡単にデモを書いてみましょう。

 パブリック静的voidメイン(String[] args) {
 BinaryLogClient クライアント = new BinaryLogClient("ホスト名", 3306, "ユーザー名", "パスワード");
 イベントデシリアライザーeventDeserializer = new EventDeserializer();
 イベントデシリアライザー.setCompatibilityMode(
 イベントデシリアライザー.互換性モード.DATE_AND_TIME_AS_LONG、
 イベントデシリアライザー.互換性モード.CHAR_AND_BINARY_AS_BYTE_ARRAY
 );
 クライアントにイベントデシリアライザーを設定します。
 クライアント.registerEventListener(新しいBinaryLogClient.EventListener() {

 @オーバーライド
 パブリック void onEvent(イベント イベント) {
 //やるべきこと
 何かを行う();
 ロガー情報(イベント.toString());
 }
 });
 クライアントに接続します。
 }

これは、公式チュートリアルに完全に従って記述されています。onEvent に独自のビジネス ロジックを記述できます。私はテスト中なので、すべてのイベントを出力します。

その後、MySQL に手動でログインし、それぞれ追加、変更、削除の操作を実行しました。監視されたログは次のとおりです。

00:23:13.331 [main] INFO util.MysqlBinLog - イベント{ヘッダー=EventHeaderV4{タイムスタンプ=0、イベントタイプ=ROTATE、サーバーId=1、ヘッダー長=19、データ長=28、次の位置=0、フラグ=32}、データ=RotateEventData{binlogFilename='mysql-bin.000001'、binlogPosition=886}}
00:23:13.334 [main] INFO util.MysqlBinLog - イベント{header=EventHeaderV4{timestamp=1556468403000、eventType=FORMAT_DESCRIPTION、serverId=1、headerLength=19、dataLength=100、nextPosition=0、flags=0}、data=FormatDescriptionEventData{binlogVersion=4、serverVersion='5.7.23-0ubuntu0.16.04.1-log'、headerLength=19、dataLength=95}}
00:23:23.715 [main] INFO util.MysqlBinLog - イベント{ヘッダー=EventHeaderV4{タイムスタンプ=1556468603000、イベントタイプ=ANONYMOUS_GTID、サーバーID=1、ヘッダー長=19、データ長=46、次の位置=951、フラグ=0}、データ=null}
00:23:23.716 [main] INFO util.MysqlBinLog - イベント{header=EventHeaderV4{timestamp=1556468603000、eventType=QUERY、serverId=1、headerLength=19、dataLength=51、nextPosition=1021、flags=8}、data=QueryEventData{threadId=4、executionTime=0、errorCode=0、database='pf'、sql='BEGIN'}}
00:23:23.721 [main] INFO util.MysqlBinLog - イベント{header=EventHeaderV4{timestamp=1556468603000、eventType=TABLE_MAP、serverId=1、headerLength=19、dataLength=32、nextPosition=1072、flags=0}、data=TableMapEventData{tableId=108、database='pf'、table='student'、columnTypes=15、3、columnMetadata=135、0、columnNullability={}}}
00:23:23.724 [main] INFO util.MysqlBinLog - イベント{ヘッダー=EventHeaderV4{タイムスタンプ=1556468603000、イベントタイプ=EXT_WRITE_ROWS、サーバーId=1、ヘッダー長=19、データ長=23、次の位置=1114、フラグ=0}、データ=WriteRowsEventData{テーブルId=108、含まれる列={0、1}、行=[
[[B@546a03af, 2]
]}}
00:23:23.725 [main] INFO util.MysqlBinLog - イベント{ヘッダー=EventHeaderV4{タイムスタンプ=1556468603000、イベントタイプ=XID、サーバーId=1、ヘッダー長=19、データ長=12、次の位置=1145、フラグ=0}、データ=XidEventData{xid=28}}
00:23:55.872 [main] INFO util.MysqlBinLog - イベント{ヘッダー=EventHeaderV4{タイムスタンプ=1556468635000、イベントタイプ=ANONYMOUS_GTID、サーバーID=1、ヘッダー長=19、データ長=46、次の位置=1210、フラグ=0}、データ=null}
00:23:55.872 [main] INFO util.MysqlBinLog - イベント{header=EventHeaderV4{timestamp=1556468635000、eventType=QUERY、serverId=1、headerLength=19、dataLength=51、nextPosition=1280、flags=8}、data=QueryEventData{threadId=4、executionTime=0、errorCode=0、database='pf'、sql='BEGIN'}}
00:23:55.873 [main] INFO util.MysqlBinLog - イベント{ヘッダー=EventHeaderV4{タイムスタンプ=1556468635000、イベントタイプ=TABLE_MAP、サーバーId=1、ヘッダー長=19、データ長=32、次の位置=1331、フラグ=0}、データ=TableMapEventData{テーブルId=108、データベース='pf'、テーブル='student'、列タイプ=15、3、列メタデータ=135、0、列Nullability={}}}
00:23:55.875 [main] INFO util.MysqlBinLog - イベント{ヘッダー=EventHeaderV4{タイムスタンプ=1556468635000、イベントタイプ=EXT_UPDATE_ROWS、サーバーId=1、ヘッダー長=19、データ長=31、次の位置=1381、フラグ=0}、データ=UpdateRowsEventData{テーブルId=108、含まれている列のBeforeUpdate={0, 1}、含まれている列={0, 1}、行=[
{前=[[B@6833ce2c, 1]、後=[[B@725bef66, 3]}
]}}
00:23:55.875 [main] INFO util.MysqlBinLog - イベント{ヘッダー=EventHeaderV4{タイムスタンプ=1556468635000、イベントタイプ=XID、サーバーId=1、ヘッダー長=19、データ長=12、次の位置=1412、フラグ=0}、データ=XidEventData{xid=41}}
00:24:22.333 [main] INFO util.MysqlBinLog - イベント{ヘッダー=EventHeaderV4{タイムスタンプ=1556468662000、イベントタイプ=ANONYMOUS_GTID、サーバーID=1、ヘッダー長=19、データ長=46、次の位置=1477、フラグ=0}、データ=null}
00:24:22.334 [main] INFO util.MysqlBinLog - イベント{header=EventHeaderV4{timestamp=1556468662000、eventType=QUERY、serverId=1、headerLength=19、dataLength=51、nextPosition=1547、flags=8}、data=QueryEventData{threadId=4、executionTime=0、errorCode=0、database='pf'、sql='BEGIN'}}
00:24:22.334 [main] INFO util.MysqlBinLog - イベント{ヘッダー=EventHeaderV4{タイムスタンプ=1556468662000、イベントタイプ=TABLE_MAP、サーバーId=1、ヘッダー長=19、データ長=32、次の位置=1598、フラグ=0}、データ=TableMapEventData{テーブルId=108、データベース='pf'、テーブル='student'、列タイプ=15、3、列メタデータ=135、0、列Nullability={}}}
00:24:22.335 [main] INFO util.MysqlBinLog - イベント{ヘッダー=EventHeaderV4{タイムスタンプ=1556468662000、イベントタイプ=EXT_DELETE_ROWS、サーバーId=1、ヘッダー長=19、データ長=23、次の位置=1640、フラグ=0}、データ=DeleteRowsEventData{テーブルId=108、含まれる列={0、1}、行=[
[[B@1888ff2c, 3]
]}}
00:24:22.335 [main] INFO util.MysqlBinLog - イベント{ヘッダー=EventHeaderV4{タイムスタンプ=1556468662000、イベントタイプ=XID、サーバーId=1、ヘッダー長=19、データ長=12、次の位置=1671、フラグ=0}、データ=XidEventData{xid=42}}

独自のビジネスに合わせて、より優れたカスタマイズされたツールクラスをカプセル化します

最初はコードを投稿するつもりでしたが、、、コードがどんどん長くなっていったのでGitHubにアップロードすることにしました。ここでは実装の一部だけ投稿します。コード転送ポータル

実装のアイデア

  1. すべてのデータベース内のすべてのデータ テーブルを監視する必要はないため、単一のテーブルの監視をサポートします。
  2. 複数のスレッドで使用できます。
  3. 監視対象のコンテンツを好みの形式に変換します (記事のデータ構造はあまり良くないかもしれませんが、これより適切なものは思いつきません)。

したがって、実装のアイデアはおおよそ次のようになります。

  1. クライアントをカプセル化し、外部には取得メソッドのみを提供し、初期化の詳細コードは隠蔽します。
  2. リスナー (疑似) を登録するメソッドを提供します。これにより、テーブルのリスナーを登録できます (リスナー インターフェイスを再定義し、登録されているすべてのリスナーはこれを実装するだけで済みます)。
  3. 唯一の実際のリスナーはクライアントであり、クライアントはこのデータベース インスタンス上のすべての操作をリッスンし、それらを目的の形式の LogItem に変換して、ブロッキング キューに配置します。
  4. 複数のスレッドを開始し、ブロッキング キューを消費し、LogItem に対応するデータ テーブル リスナーを呼び出して、ビジネス ロジックを実行します。

初期化コード:

 パブリックMysqlBinLogListener(Conf conf) {
 BinaryLogClient クライアント = new BinaryLogClient(conf.host、conf.port、conf.username、conf.passwd);
 イベントデシリアライザーeventDeserializer = new EventDeserializer();
 イベントデシリアライザー.setCompatibilityMode(
 イベントデシリアライザー.互換性モード.DATE_AND_TIME_AS_LONG、
 イベントデシリアライザー.互換性モード.CHAR_AND_BINARY_AS_BYTE_ARRAY
 );
 クライアントにイベントデシリアライザーを設定します。
 this.parseClient = クライアント;
 this.queue = 新しいArrayBlockingQueue<>(1024);
 conf を次のように変更します。
 リスナー = 新しい ConcurrentHashMap<>();
 dbTableCols = 新しいConcurrentHashMap<>();
 this.consumer = Executors.newFixedThreadPool(consumerThreads);
 }

登録コード:

 パブリック void regListener(String db、String table、BinLogListener listener) 例外をスローします {
 文字列 dbTable = getdbTable(db, table);
 クラス.forName("com.mysql.jdbc.Driver");
 // 現在登録されているテーブルの列情報を保存します。Connection connection = DriverManager.getConnection("jdbc:mysql://" + conf.host + ":" + conf.port, conf.username, conf.passwd);
 Map<String, Colum> cols = getColMap(connection, db, table);
 dbTableCols.put(dbTable、列);

 //現在登録されているリスナーを保存する
 リスト<BinLogListener> リスト = listeners.getOrDefault(dbTable, 新しい ArrayList<>());
 リストに追加します(リスナー)。
 listeners.put(dbTable、リスト);
 }

このステップでは、リスナーを登録しながらテーブルのスキーマ情報を取得し、それをマップに保存して、後続のデータ処理を容易にします。

リスニングコード:

 @オーバーライド
 パブリック void onEvent(イベント イベント) {
 イベントタイプ eventType = event.getHeader().getEventType();

 イベントタイプ == イベントタイプ.TABLE_MAP) {
 テーブルマップイベントデータ tableData = event.getData();
 文字列 db = tableData.getDatabase();
 文字列テーブル = tableData.getTable();
 dbTable = getdbTable(db、テーブル);
 }

 // 追加、削除、更新の3つの操作のみを処理します if (isWrite(eventType) || isUpdate(eventType) || isDelete(eventType)) {
 if (isWrite(イベントタイプ)) {
 WriteRowsEventData データ = event.getData();
 (Serializable[] 行: data.getRows()) の場合 {
  dbTableCols.containsKey(dbTable) の場合 {
  LogItem e = LogItem.itemFromInsert(行、dbTableCols.get(dbTable));
  e.setDbTable(dbTable);
  キューに追加します。
  }
 }
 }
 }
 }

面倒なので、ここでは追加操作の処理のみを実装し、他の操作は書いていません。

消費コード:

 パブリック void parse() は IOException をスローします {
 parseClient.registerEventListener(これを);

 (int i = 0; i < consumerThreads; i++) の場合 {
 消費者.送信(() -> {
 (真)の間{
  キューサイズ() > 0 の場合
  試す {
  ログアイテム項目 = queue.take();
  文字列 dbtable = item.getDbTable();
  リスナー.get(dbtable).forEach(l -> {
  l.onEvent(アイテム);
  });

  } キャッチ (InterruptedException e) {
  e.printStackTrace();
  }
  }
  スレッドをスリープ状態にします(1000);
 }
 });
 }
 parseClient.connect();
 }

消費するときは、キューからアイテムを取得し、対応する 1 つ以上のリスナーを取得してそれぞれアイテムを消費します。

テストコード:

 パブリック静的void main(String[] args)は例外をスローします{
 Conf conf = 新しい Conf();
 conf.host = "ホスト名";
 conf.port = 3306;
 conf.username = conf.passwd = "hhsgsb";

 MysqlBinLogListener を新しい MysqlBinLogListener(conf);
 mysqlBinLogListener.parseArgsAndRun(引数);
 mysqlBinLogListener.regListener("pf", "学生", 項目 -> {
 System.out.println(新しい文字列((byte[])item.getAfter().get("name")));
 logger.info("{} に挿入、値 = {}", item.getDbTable(), item.getAfter());
 });
 mysqlBinLogListener.regListener("pf", "teacher", item -> System.out.println("teacher ===="));

 mysqlBinLogListener.parse();
 }

この短いコードでは、2 つのリスナーが登録されており、それぞれ学生テーブルと教師テーブルをリッスンし、それぞれを印刷します。テスト後、教師テーブルにデータを挿入するときに、定義されたビジネス ロジックを個別に実行できます。

注: ここでのツール クラスは、多くの例外処理が行われていないため直接使用できません。関数は挿入ステートメントのみをリッスンしますが、これは実装の参照として使用できます。

参考文献

  • github.com/shyiko/mysq…
  • https://www.jb51.net/article/166761.htm

要約する

以上がこの記事の全内容です。この記事の内容が皆様の勉強や仕事に何らかの参考学習価値をもたらすことを願います。123WORDPRESS.COM をご愛顧いただき、誠にありがとうございます。

以下もご興味があるかもしれません:
  • MySQL シリーズ: redo ログ、undo ログ、binlog の詳細な説明
  • MySQL binlog_ignore_dbパラメータの具体的な使用法
  • MySQL で binlog を使用する際のフォーマットの選択方法
  • MySQLを監視するためのbinlogログ解析ツールの詳しい説明:Canal
  • MySQL 8.0 の binlog の詳細な説明
  • MYSQL の binlog 最適化に関する考察の要約
  • MySQLデータベースのbinlogクリーンアップコマンドの詳細な説明
  • MySQL の innodb_flush_log_at_trx_commit と sync_binlog を区別する方法

<<:  最も完全なpackage.json分析

>>:  VMware 15 を使用して仮想マシンをインストールし、CentOS 8 を使用する詳細な手順

推薦する

Ubuntu 20.04 では、隠し録音ノイズ低減機能が有効になります (推奨)

最近、 Ubuntu 20.04でkazamを使用して録音しているときに、問題が見つかりました。シス...

間違った MySQL コマンドをキャンセルしたい場合はどうすればいいですか?

間違った mysql コマンドを入力したのでキャンセルしたいです。どうすればいいですか? ctrl ...

JavaScriptはシンプルな日付効果を実装します

JavaScriptの日付エフェクトの具体的なコードは参考用です。具体的な内容は次のとおりです。コー...

意外と知らないLinuxのSSHコマンドの使い方7選を徹底解説

システム管理者は複数のサーバーを同時に管理する場合があり、これらのサーバーは異なる場所に配置されてい...

CSS3 フレックスレイアウトを使用して要素を均等に分散するサンプルコード

この記事では主に、CSS3 フレックスレイアウトを使用して要素を均等に配置する方法を紹介します。自分...

ウェブページの画像の回転を実現するjs

この記事では、Webページの画像の回転を実現するためのjsの具体的なコードを参考までに共有します。具...

JavaScriptの基礎を学ぶ

目次1. JavaScriptを記述する場所2. JavaScriptでよく使われる入力文と出力文1...

Windows Server 2008 R2 に MySQL 5.7.10 をインストールする手順

MSIインストールパッケージを使用してインストールするご使用のオペレーティング システムに応じて、対...

JavaScript ウェブページ入門開発詳細説明

パート3: ❤バックエンドデータ受信を見落とす3つの方法❤ (おすすめ集)パート 2: Web フォ...

HTML のタイトル、段落、改行、水平線、特殊文字についての簡単な説明

タイトルXML/HTML コードコンテンツをクリップボードにコピー< h1 >第 1 レ...

Linuxはnode.jsを完全に削除し、yumコマンドで再インストールします。

最初のステップ組み込みのパッケージ管理機能で一度削除する yum 削除 nodejs npm -y ...

MySQL テーブルの断片化を解消し、スペースを再利用する方法

目次MySQL テーブルの断片化の原因行の断片化行内断片化空き領域の断片化MySQL で極度に断片化...

JavaScriptの再帰の詳細

目次1. 再帰とは何ですか? 2. 再帰を使って数学の問題を解く1. 1 * 2 * 3 * 4 …...

5つのクールで実用的なHTMLタグと属性の紹介

実はこれもクリックベイトのタイトルであり、「派手」とは言えません。ただ私が無知で、こうしたラベルを見...