地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【書評】Learning OpenTelemetryを読んだ

OpenTelemetryプロジェクトの共同設立者の一人である「Ted Young」が書いたOpenTelemetryの本。ちょうどOpenTelemetryを導入している時期というのもあってプロジェクトの設立者がどういったことを考えて進めているのかについて興味があったので読んでみた。

github.com

OpenTelemetryの導入までの一冊としても導入した後にさらに知りたい人にも良さそうな本であった。observabilityとはから始まりなぜOpenTelemetryが必要となったのかという背景の説明があったりと「なんとなく理解していたメリット」がこういった立場の方が言語化してくれたおかげで自分の中でもより一層しっくりきた。

特に良かったのは9章の「Chapter 9. Rolling Out Observability」でこの章ではテレメトリが観測可能性の重要な部分である一方で、組織、チーム、またはプロジェクトへの観測可能性の展開に自体が十分であるわけではないことを強調しています。この章は、観測可能性が組織を変革し、ソフトウェアのパフォーマンスがビジネスの健全性にどのように翻訳されるかについての共有言語と理解を提供する能力において真の価値があるという観点から、幅広いターゲットを対象に書かれています。

組織がオブザーバビリティを実装する際の戦略、組織のコミットメント、そして有意義な改善を達成するために単なるデータ収集を超える必要性を強調するための良いガイドとして自身の組織への直ぐに応用できそうと感じました。

以下はChatGPTによる要約

  • 観測可能性はデータ収集だけではなく、ソフトウェアシステムと組織プロセスを理解し改善する価値についてです。
  • 観測可能性を実装するには、プロセス、実践、および意思決定へのデータ使用方法に関する組織全体のコミットメントが必要です。
  • 展開時に検討すべき「観測可能性の3つの軸」を概説しています:深い対広い(システムの特定の部分から詳細な情報を収集するか、システム全体に関する多くのデータを最初に取得するか)、コードの書き換え対収集の書き換え(新しい計測ツールを既存または新しいサービスに追加する努力をするか、既存のデータを新しい形式に変換するか)、集中化対分散化(強力な中央観測可能性チームを作るか、より軽いタッチを使用するか)。
  • 組織が特定のニーズと課題に合わせて観測可能性戦略を調整することの重要性を示す事例研究が提示されています。
  • 明確な目標から始め、最も即座に価値を提供できる場所に努力を集中させ、徐々に組織全体に観測可能性の実践を拡大することの重要性を強調しています。
  • 観測可能性は信頼や透明性と同様の重要な価値観であり、より良いソフトウェアシステムとチームを構築するための手段として提示されています。

ナレッジベースや新旧システムでの移行の話は直ぐにでも取り組めそうなものなのでやっていく

【MySQL】explain format=jsonが便利

yakst.com

explainのjsonフォーマットは5.7から存在します。explain format=json ${クエリ}と実行することでjson形式でexplainを表示することが出来ます。これの嬉しいポイントとしてはreadやevalに対するコストを表示してくれます。以下は実際の例です。

              "cost_info": {
                "read_cost": "707.25",
                "eval_cost": "0.54",
                "prefix_cost": "990.15",
                "data_read_per_join": "25K"
              },

それぞれの簡単な説明を記載します。

  • read_cost: 707.25

このコストは、データをディスクから読み込む操作の推定コストを表します。高い値は、物理的なディスクアクセスが多いことを意味し、特に大量のデータを扱うクエリでパフォーマンスに影響を与える可能性があります。

  • eval_cost: 0.54

評価コストとは、フィルタ条件や計算式の評価に要する推定コストを指します。この値は通常、比較的低いものですが、複雑な計算や関数が多用されている場合は増加する可能性があります。

  • prefix_cost: 990.15

この操作およびそれ以前のクエリ部分全体の累積コストです。この値は、クエリのこの部分までの全ての操作のコストを合計したもので、クエリの特定の部分が全体のコストにどの程度影響しているかを示します。

  • data_read_per_join: 25K

この結合操作によって読み込まれるデータ量の推定値です。この場合、約25キロバイトのデータが読み込まれると見積もられています。この指標は、クエリがデータベースからどれだけのデータを取得するかを示し、データベースのI/O負荷の指標として役立ちます。

【MySQL】trx_sys->mutexは人気のmutex

MySQLでtrx_sys->mutexは色々な箇所から呼び出されている。これを長時間保持してるトランザクションがあるとMySQL全体の性能ダウンに繋がるというお話。

まとめ

  • trx_sys->mutexはトランザクションの開始/終了時にも確保する必要がある
  • 長時間保持して解放しないスレッドがいると他トランザクションの処理が進まないという状態になる
  • 長時間保持するようなケースは実行頻度を減らすか代替案があればそちらを使用するにして回避する

詳細

github.com

実装自体はこんな感じ

github.com

InnoDBSQLを実行する際はトランザクションで実行されます。各トランザクションが開始された後、そのトランザクションをグローバル トランザクション リストに追加する必要があり、グローバル トランザクション リストは trx_sys->mutex ミューテックスによって保護される必要があります。

/** Starts a transaction. */
static void trx_start_low(
    trx_t *trx,      /*!< in: transaction */
    bool read_write) /*!< in: true if read-write transaction */
{
    if (!trx_is_autocommit_non_locking(trx)) { // 自動コミットモードじゃないかつ非ロッキング読み取りクエリ
      /* If this is a read-only transaction that is writing
      to a temporary table then it needs a transaction id
      to write to the temporary table. */

      if (read_write) {
        trx_sys_mutex_enter();

        ut_ad(!srv_read_only_mode);

        trx->state.store(TRX_STATE_ACTIVE, std::memory_order_relaxed);

        trx->id = trx_sys_allocate_trx_id();

        trx_sys->rw_trx_ids.push_back(trx->id);

        trx_sys_mutex_exit();

        trx_sys_rw_trx_add(trx);

      } else {
        trx->state.store(TRX_STATE_ACTIVE, std::memory_order_relaxed);
      }

このコードは、MySQLデータベースのInnoDBストレージエンジン内部でトランザクションを開始するための関数trx_start_lowの実装を示しています。この関数は、新しいトランザクションを開始する際に、トランザクションオブジェクトの初期化や状態の更新など、いくつかの重要なステップを行います。以下に、コードの主要な部分を説明します。

github.com

【MySQL】8.0で使えるperformance_schema.data_locksの中身

(`wait_started`,
 `wait_age`,
 `wait_age_secs`,
 `locked_table`,
 `locked_table_schema`,
 `locked_table_name`,
 `locked_table_partition`,
 `locked_table_subpartition`,
 `locked_index`,
 `locked_type`,
 `waiting_trx_id`,
 `waiting_trx_started`,
 `waiting_trx_age`,
 `waiting_trx_rows_locked`,
 `waiting_trx_rows_modified`,
 `waiting_pid`,
 `waiting_query`,
 `waiting_lock_id`,
 `waiting_lock_mode`,
 `blocking_trx_id`,
 `blocking_pid`,
 `blocking_query`,
 `blocking_lock_id`,
 `blocking_lock_mode`,
 `blocking_trx_started`,
 `blocking_trx_age`,
 `blocking_trx_rows_locked`,
 `blocking_trx_rows_modified`,
 `sql_kill_blocking_query`,
 `sql_kill_blocking_connection`) AS
SELECT `r`.`trx_wait_started` AS `wait_started`,
       timediff(now(),`r`.`trx_wait_started`) AS `wait_age`,
       timestampdiff(SECOND,`r`.`trx_wait_started`,now()) AS `wait_age_secs`,
       concat(`sys`.`quote_identifier`(`rl`.`OBJECT_SCHEMA`),'.',`sys`.`quote_identifier`(`rl`.`OBJECT_NAME`)) AS `locked_table`,
       `rl`.`OBJECT_SCHEMA` AS `locked_table_schema`,
       `rl`.`OBJECT_NAME` AS `locked_table_name`,
       `rl`.`PARTITION_NAME` AS `locked_table_partition`,
       `rl`.`SUBPARTITION_NAME` AS `locked_table_subpartition`,
       `rl`.`INDEX_NAME` AS `locked_index`,
       `rl`.`LOCK_TYPE` AS `locked_type`,
       `r`.`trx_id` AS `waiting_trx_id`,
       `r`.`trx_started` AS `waiting_trx_started`,
       timediff(now(),`r`.`trx_started`) AS `waiting_trx_age`,
       `r`.`trx_rows_locked` AS `waiting_trx_rows_locked`,
       `r`.`trx_rows_modified` AS `waiting_trx_rows_modified`,
       `r`.`trx_mysql_thread_id` AS `waiting_pid`,
       `sys`.`format_statement`(`r`.`trx_query`) AS `waiting_query`,
       `rl`.`ENGINE_LOCK_ID` AS `waiting_lock_id`,
       `rl`.`LOCK_MODE` AS `waiting_lock_mode`,
       `b`.`trx_id` AS `blocking_trx_id`,
       `b`.`trx_mysql_thread_id` AS `blocking_pid`,
       `sys`.`format_statement`(`b`.`trx_query`) AS `blocking_query`,
       `bl`.`ENGINE_LOCK_ID` AS `blocking_lock_id`,
       `bl`.`LOCK_MODE` AS `blocking_lock_mode`,
       `b`.`trx_started` AS `blocking_trx_started`,
       timediff(now(),`b`.`trx_started`) AS `blocking_trx_age`,
       `b`.`trx_rows_locked` AS `blocking_trx_rows_locked`,
       `b`.`trx_rows_modified` AS `blocking_trx_rows_modified`,
       concat('KILL QUERY ',`b`.`trx_mysql_thread_id`) AS `sql_kill_blocking_query`,
       concat('KILL ',`b`.`trx_mysql_thread_id`) AS `sql_kill_blocking_connection`
FROM ((((`performance_schema`.`data_lock_waits` `w`
         JOIN `information_schema`.`INNODB_TRX` `b` on((`b`.`trx_id` = cast(`w`.`BLOCKING_ENGINE_TRANSACTION_ID` AS char
                                                                            CHARSET utf8mb4))))
        JOIN `information_schema`.`INNODB_TRX` `r` on((`r`.`trx_id` = cast(`w`.`REQUESTING_ENGINE_TRANSACTION_ID` AS char
                                                                           CHARSET utf8mb4))))
       JOIN `performance_schema`.`data_locks` `bl` on((`bl`.`ENGINE_LOCK_ID` = `w`.`BLOCKING_ENGINE_LOCK_ID`)))
      JOIN `performance_schema`.`data_locks` `rl` on((`rl`.`ENGINE_LOCK_ID` = `w`.`REQUESTING_ENGINE_LOCK_ID`)))
ORDER BY `r`.`trx_wait_started`