idsテーブルの話【MIRACLE ZBX 1.8, 2.0, 2.2】
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();
以前の記事で書いたと思うのですが、上記の部分で下記のことを行っています。
- アイテム情報の更新
- ヒストリ情報の書き込み
- トリガー評価
- トレンド情報の書き込み
まず、「ヒストリ情報の書き込み」に着目します。追っていく関数は、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テーブルもリストに追加され、キャッシュ(オンメモリ)で管理されるようになっています。上記のボトルネックが解消されているということです。