現在位置: ホーム / みらくるブログ / idsテーブルの話【MIRACLE ZBX 1.8, 2.0, 2.2】

idsテーブルの話【MIRACLE ZBX 1.8, 2.0, 2.2】

各テーブルの最大IDを保存している「idsテーブル」というものがあります。ボトルネックの要因となりやすいです。

idsテーブルって何?

こんにちは。ミラクル・リナックスの「花島タケシ」(ペンネーム)です。
最初に断っておきますが、今回はいまいちまとまりのない内容になってしまいます。

 

Zabbixが使用するDBのテーブルに、「idsテーブル」といったものがあります。

これは、各テーブルで使用するIDに関するカラムの最大値を保存するためのものです。

そして、Zabbix内において各テーブルの要素を増やすときに、このテーブルにアクセスを行い、所定の値を増やした後に要素を追加するという実装になっています。(基本的には)

 

ソースコードを見ていこう!

あえて、最新版でないMIRACLE ZBX 2.0.15のコードを見ていきます。

DB Syncerプロセスが、キャッシュからDBへと書き出す一連の処理(src/libs/zbxdbcache/dbcache.c : DCsync_history())から眺めていきます。

2139 int DCsync_history(int sync_type)
2140 {
...
2317     DBbegin();
2318
2319     if (0 != (daemon_type & ZBX_DAEMON_TYPE_SERVER))
2320     {
2321       DCmass_update_items(history, history_num);
2322       DCmass_add_history(history, history_num);
2323       DCmass_update_triggers(history, history_num);
2324       DCmass_update_trends(history, history_num);
2325     }
2326     else
2327     {
2328       DCmass_proxy_add_history(history, history_num);
2329       DCmass_proxy_update_items(history, history_num);
2330     }
2331
2332     DBcommit();

 

以前の記事で書いたと思うのですが、上記の部分で下記のことを行っています。

  1. アイテム情報の更新
  2. ヒストリ情報の書き込み
  3. トリガー評価
  4. トレンド情報の書き込み

 

まず、「ヒストリ情報の書き込み」に着目します。追っていく関数は、DCmass_add_history()となります。

1711 static void DCmass_add_history(ZBX_DC_HISTORY *history, int history_num)
1712 {
...
1746   /* history */
1747   if (0 != h_num)
1748     dc_add_history_sql(history, history_num, &sql_offset);
1749
1750   /* history_uint */
1751   if (0 != huint_num)
1752     dc_add_history_uint_sql(history, history_num, &sql_offset);
1753
1754   /* history_str */
1755   if (0 != hstr_num)
1756     dc_add_history_str_sql(history, history_num, &sql_offset);
1757
1758   /* history_text */
1759   if (0 != htext_num)
1760     dc_add_history_text_sql(history, history_num, &sql_offset, htext_num);
1761
1762   /* history_log */
1763   if (0 != hlog_num)
1764     dc_add_history_log_sql(history, history_num, &sql_offset, hlog_num);
...

 

保存する、ヒストリデータに応じた関数をコールしているわけですが、ログ情報に着目します。

1654 static void dc_add_history_log_sql(ZBX_DC_HISTORY *history, int history_num, size_t *sql_offset, int hlog_num)
1655 {
1656   int   i;
1657   const char  *ins_history_log_sql =
1658       "insert into history_log"
1659       " (id,itemid,clock,timestamp,source,severity,value,logeventid,ns)"
1660       " values ";
1661   zbx_uint64_t  id;
1662   char    *value_esc, *source_esc;
1663
1664   id = DBget_maxid_num("history_log", hlog_num);

 

漸く、今回着目するDBget_maxid_num()関数が現れました。この関数は別のソースファイル(src/libs/zbxdbhigh/db.c)に記述されております。

1286 zbx_uint64_t  DBget_maxid_num(const char *tablename, int num)
1287 {
1288   if (0 == strcmp(tablename, "history_log") ||
1289       0 == strcmp(tablename, "history_text") ||
1290       0 == strcmp(tablename, "dservices") ||
1291       0 == strcmp(tablename, "dhosts") ||
1292       0 == strcmp(tablename, "alerts") ||
1293       0 == strcmp(tablename, "escalations") ||
1294       0 == strcmp(tablename, "autoreg_host") ||
1295       0 == strcmp(tablename, "graph_discovery") ||
1296       0 == strcmp(tablename, "trigger_discovery"))
1297     return DCget_nextid(tablename, num);
1298
1299   return DBget_nextid(tablename, num);
1300 }

 

DBget_maxid_num()は見てのように、特定のテーブルに対しては、DCget_nextid()をコールし、それ以外はDBget_nexid()をコールするようになっています。

 

次に、かなり巻戻りますが、「トリガー評価」について追っていきます。着目する関数は、DCmass_update_triggers()となります。

 777 static void DCmass_update_triggers(ZBX_DC_HISTORY *history, int history_num)
778 {
...
849   if (0 != events_num)
850   {
851     zbx_uint64_t  eventid;
852
853     eventid = DBget_maxid_num("events", events_num);
854
855     for (i = 0; i < trigger_order.values_num; i++)
856     {
857       trigger = (DC_TRIGGER *)trigger_order.values[i];
858
859       if (1 != trigger->add_event)
860         continue;
861
862       process_event(eventid++, EVENT_SOURCE_TRIGGERS, EVENT_OBJECT_TRIGGER, trigger->triggerid,
863           &trigger->timespec, trigger->new_value, trigger->value_changed, 0, 0);
864     }
865
866     DBflush_itservice_updates();
867   }

 

トリガー判定が真となり、イベントを生成する必要があったときに、同じようにDBget_maxid_num()をコールしています。その後、ループで一つずつprocess_event()をコールし、イベントを生成しています。

 

DCget_nextid() と DBget_nextid() の違い

さくっと結論を書くと、DCget_nextid()はキャッシュからの取得で、DBget_nextid()はDBからの取得となります。(なので、最初に"基本的に"と書きました。)

前節において、ヒストリ情報ではDCget_nextid()をコールしておりますが、トリガー評価ではDBget_nextid()をコールしています。

そして、これら一連の処理は、DBbegin(); から DBcommit(); までのトランザクション中で行われるということです。つまり、各DB Syncerプロセスが書き出しを行うときに、イベント生成が発生すると競合が発生するためにボトルネックとなりやすくなります。

 

2.2では改修されていますよ

2.2のDBget_maxid_num()では、eventsテーブルもリストに追加され、キャッシュ(オンメモリ)で管理されるようになっています。上記のボトルネックが解消されているということです。