インフラエンジニアとしてキャリアをスタートさせたばかりの頃、私はある日、逃れられない大きな壁に直面しました。それは、 アプリケーションエンジニアが直せないと言い放った「パフォーマンス地獄」 です。
インフラ担当の私が、夜間の二重生活を通じてSQLを読み書きできるようになり、プロジェクトの危機を救うまでの生々しいストーリーと、そこから得た「職能の壁を越える」ための教訓を共有します。
序章:平和なインフラ領域と、最初の地獄
入社4年目。当時の私の仕事は、Oracle Databaseの起動・停止、パラメータチューニング、バックアップ・リカバリ1といった、インフラ視点のコマンドライン操作が中心でした。SQLはDCL(データ制御言語)2の一部を使う程度で、アプリケーションのビジネスロジックを読み解くDML(データ操作言語)3には手を出していませんでした。
しかし、最初のトラブルは突然訪れました。
突如訪れた「メモリの壁」
サービスイン直前に、PMの判断でメモリ増設を試みましたが、当時のOSであるWindows Server 2008 R2のエディションの制約で増設分を認識しないという事態が発生。急遽エディションをStandardからEnterpriseにアップグレードするという、前代未聞の事件がありました。この増設によりOracleのパフォーマンスは一時的に改善し、数カ月は平穏が保たれました。
Windows Serverのエディションはアップグレードインストールができるのですが、自宅の仮想環境で実証済みとは言えサービスイン直前でしたから痺れましたね。
第1章:「月次処理地獄」と突きつけられた拒絶
数カ月後、システムが本格的に稼働し、データが蓄積され始めた頃、顧客から厳しいクレームが入りました。
「月次処理の期間になるとパフォーマンスが著しく低下し、業務時間内に締め処理を行えない。取引先への支払遅延など、非常にまずい状況だ」
顧客からの要求は「来月までに改善」という厳しいものでした。
データが示す原因の特定
私はすぐに、サービスイン前に導入していたパフォーマンス分析ツール STATSPACK4の定期スナップショットを読み込みました。当時はGeminiのような生成AIは存在しないため、翔泳社の『新・門外不出のOracle現場ワザ』を文字通り読み込み、地道に仮説を立てていきました。
分析の結果、以下の異常が判明しました。
- 待機イベント: db file sequential read5 が非常に多く、DB Time6のほとんどを占めている。
- SQLの負荷: Physical Read が極端に多い50以上のSQLが存在する。
これは、「データベースがメモリ(SGA/PGA)7ではなくディスクへの読み書きに大量の時間を費やしている」、つまり**「ディスクI/O」がボトルネック**であることを示していました。
「無責任の壁」の誕生
私は分析結果をプロジェクトマネージャー(PM)に報告し、原因となっているSQLのチューニングを下請けのアプリケーションエンジニアに依頼しました。SQLチューニングは、アプリケーションのビジネスロジックを理解しているアプリ側の担当である、と当然思っていたからです。
しかし、返ってきた答えは予想外のものでした。
- 「今までこれで動いてきたからSQLには問題ない」
- 「データベースのチューニングで解決できるのではないか」
- 「うちでは原因は分からないし、通常の保守業務で工数的に手一杯であり対応できない」
彼らには 「調べすらしてもらえませんでした」 。
私は驚きましたが、顧客と下請け業者の間に不信感があった経緯から自社のPMが間に入っていたこともあり、最終的にPMが下した決断は、
「SQLを読み書きできないお前(私)が、通常業務後にチューニングしろ」
というものでした。
第2章:背水の陣の1ヶ月間とSQLとの対峙
この無茶な決定により、私の 1ヶ月間にわたる「二重生活」 が始まりました。私の知識はインフラに留まっており、アプリケーションのSQLの読み書きができない状態からのスタートでした。
実行計画という名の「地図」の解読
STATSPACKで問題SQLは分かっても、なぜ遅いのか?を解明するため、私は問題のあるSQLの実行計画8を確認しました。すると、多くのSQLで TABLE ACCESS FULL9というアクセスパターンが非常に多いことが判明しました。
ここから私の学習が始まります。
- SQLの読み解き: まずは複雑に結合された FROM 句と、テーブル結合順を照らし合わせるところから始めました。SQLが読めないため、ER図(当時は存在を知らなかった)に相当する図を自力で書き出して、テーブルの関連性を把握しました。
- 検証の自動化: STATSPACKのレポート作成は時間がかかるため、SQL*PLUSで SET AUTOTRACE ON10 を実行し、実行計画を即座に確認できる検証サイクルを確立しました。
- 分離と再構築: テスト環境で、複雑な結合SQLを分割し、どこで TABLE ACCESS FULL が発生するかを少しずつ元のSQLに戻しながら切り分けました。
この「生きているビジネスロジックのSQLを読み書きする」という経験を通じて、私は文字通りSQLを身体で覚えていきました。
悟りの瞬間:「大丈夫だった」のではなく「データが少なかった」
検証の過程で、私は最も深刻なボトルネックを発見し、衝撃を受けました。
それは、多くのSQLの結合条件に含まれていた、 「削除フラグが1でない」 というたった1行の条件です。
- 型の闇: 削除フラグの列が、なんと文字列型でした。このため、比較演算子(<>1)を使うと暗黙の型変換11が発生し、インデックスが使えない状態になっていた可能性がありました。
- 選択率の罠: 削除フラグにはインデックスがありましたが、ほとんどの行(9割以上)が該当する否定条件(<>1)を使うと、Oracleのオプティマイザはインデックススキャンよりテーブルフルスキャンの方が早いと判断します。
私はこのとき、悟りました。
今まで大丈夫だったのは、Oracleがいい感じの実行計画を選んでいたのではなくて、総データ数が少なかったから影響が小さかったのだ。そして今、メモリに乗り切らないだけのデータ数になったのだ。
問題の根本は、 「データ量の増大」というインフラの現実と、「SQL設計の甘さ」 が重なったところにありました。
第3章:解決編と「最強の武器」の獲得
原因が判明したことで、私はインフラエンジニアの立場ながら、SQLのチューニングに着手しました。
高度なチューニングによる突破
削除フラグとフルスキャンの問題を解決するために、私は当時、Oracle現場ワザの本で学んだ高度なテクニックを使いました。
- 型の統一: 削除フラグを TO_NUMBER(削除フラグ) < 1 や > 1 と書き換え、文字列を数値として扱うことを強制しました。
- ファンクションインデックスの作成: 結合キー、SELECTステートメントで登場するすべての列に加え、TO_NUMBER(削除フラグ) を含んだファンクションインデックス12を作成しました。
インデックスの選択率が90%以上という低い値であっても、テーブルフルスキャンよりも、このインデックスフルスキャン13の方がI/O効率が改善し、複雑な結合が多いSQLのパフォーマンスは大幅に向上しました。
(ちなみに、今思えば 削除フラグ <> '1' と文字列で比較するだけで、暗黙の型変換は防げたかもしれません。また自己結合も多かったためWINDOW関数14による大幅な(最大50%程度の)改善ができたかも知れません。完璧な仕事ではなかったですが、目の前の問題を解決する「生きた学び」でした。)
この対応により、その後サーバーリプレースに至るまで、大きなパフォーマンスクレームは発生しなくなりました。
結論:インフラエンジニアの「最終責任」とは何か
この1ヶ月間の地獄の経験は、私にとって最高の教材となり、「SQLを読み書きできる」というインフラエンジニアとして最強の武器を与えてくれました。
そして、この経験を通じて、私は職能の境界線と真の責任範囲について、以下のような結論に達しました。
1. 共通言語としてのSQLの不可欠性
STATSPACKのレポートを読み込むのはインフラエンジニアの範疇ですが、そこで抽出されたSQLを見て、 「このSQLはインフラから見てどう問題があるか」 を理解し、実行計画を提示するまでがインフラエンジニアの仕事です。
SQLの読み書きができなければ、その一歩を踏み出すことすらできません。
2. 真の問題解決者となる
「遅いから直せない」という無責任な壁に直面したとき、それを回避するのではなく、オーナーシップを持って解決する道を選ぶこと。
インフラエンジニアの仕事は、DBサーバーという「箱」を守ることではありません。データベースの性能を通じて、ビジネスの継続性、つまり「顧客の取引先にも迷惑をかけない」というビジネス上の責務を守り抜くことこそが、私たちの最終的な責任です。
「インフラエンジニアがSQLを使えるようになった日」は、単なるスキルアップの日ではなく、職能の壁を超えてサービス全体の安定に責任を持つという、エンジニアとしてのオーナーシップを確立した日だったのです。
-
バックアップ・リカバリ: データベースのデータを安全に保管し、障害時に元の状態に戻すための作業。 ↩
-
DCL (Data Control Language): データベースのユーザーの権限を制御する命令文(GRANT, REVOKEなど)。 ↩
-
DML (Data Manipulation Language): データベースのデータ操作を行う命令文(SELECT, INSERT, UPDATE, DELETEなど)。 ↩
-
STATSPACK: Oracle Databaseのパフォーマンス統計情報を一定間隔で収集・保存し、レポートとして出力するツール。パフォーマンス分析の基本。 ↩
-
db file sequential read: Oracleの待機イベントの一つで、インデックスを使用した読み込み、またはディスクからのデータブロックの連続読み込みを待っている状態。これが極端に多いとディスクI/Oがボトルネックであることを示唆する。 ↩
-
DB Time: データベースがユーザー要求を処理するために費やした合計時間。パフォーマンス分析の最重要指標。 ↩
-
SGA/PGA (System Global Area / Program Global Area): Oracle Databaseが使用するメモリ領域。データやSQLの実行計画などをキャッシュする重要な場所。 ↩
-
実行計画 (Execution Plan): データベースがSQL文を実行する際に、データにアクセスし、処理を実行する具体的な手順と経路を示したもの。性能チューニングの「地図」。 ↩
-
TABLE ACCESS FULL: テーブルの全行を最初から最後まで読み込むアクセス方法。大量のI/Oが発生しやすく、多くの場合は非効率とされる。 ↩
-
SET AUTOTRACE ON: SQL*PLUSというコマンドラインツールでSQLを実行する際、そのSQLの実行計画と統計情報(CPU時間、I/O回数など)を自動で表示させるコマンド。迅速な検証に必須。 ↩
-
暗黙の型変換: SQLの比較演算で、データ型が異なる場合、データベースが自動的に一方のデータ型をもう一方に合わせて変換すること。これによりインデックスが使われなくなり、フルスキャンが発生する主な原因となる。 ↩
-
ファンクションインデックス: 列の値ではなく、その列に関数(ファンクション)を適用した結果に基づいて作成されるインデックス。SQLのWHERE句で関数が使われている場合に、インデックスを使用可能にする高度なテクニック。 ↩
-
インデックスフルスキャン: インデックスの全エントリを読み込むアクセス方法。テーブル全体を読み込むよりは効率的だが、大量のデータを取得する際には発生しやすい。 ↩
-
WINDOW関数(分析関数): SQLで集合(ウィンドウ)内の行に対して計算を行い、結果を各行に返す関数(例: RANK, ROW_NUMBER, SUM/AVG overなど)。テーブルの自己結合(Self-Join)を排除し、処理を大幅に高速化できる高度なテクニック。 ↩