MySQLのエラー処理とログ記録

MySQLBeginner
オンラインで実践に進む

はじめに

この実験では、MySQLでのエラー処理とログ記録の基本を学びます。適切なエラー管理は、堅牢で保守しやすいデータベースアプリケーションを構築するうえで欠かせません。問題の診断やクエリ実行状況の把握、データの整合性の維持に役立ちます。

まず、サーバーが受信したすべてのSQL文を記録するgeneral query logを有効にします。これはデバッグや監査に便利な機能です。次に、ストアドプロシージャにDECLARE HANDLERを使ったエラー処理を実装し、予期しないエラーに適切に対処します。また、SIGNAL文でカスタムエラーを作成して発生させ、ビジネスルールを適用する方法も学びます。最後に、サーバーの動作や重大な問題に関する重要な情報が記録されるMySQLのエラーログを確認します。

この実験を終えると、MySQLのエラー処理とログ記録の基礎が身につき、より信頼性の高いデータベースソリューションを構築できるようになります。

general query logを有効にして内容を確認する

general query logには、クライアントから受信したすべてのSQL文が記録されます。デバッグや監査に非常に便利ですが、パフォーマンスに影響し、ディスク容量を大きく消費する可能性があるため、一時的な使用にとどめてください。このステップでは、ログを有効にして操作をいくつか実行し、ログファイルの内容を確認します。

まず、デスクトップからターミナルを開きます。

rootユーザーとしてMySQLサーバーに接続します。この実験環境では、sudoを使うとパスワードなしで接続できます。

sudo mysql -u root

mysql>プロンプトが表示されたら、general query logをグローバルに有効にします。

SET GLOBAL general_log = 'ON';

ログファイルは、デフォルトではMySQLのデータディレクトリに保存されます。確認しやすいように、保存先を/tmpディレクトリに変更します。

SET GLOBAL general_log_file = '/tmp/mysql_general.log';

次のコマンドを実行して、保存先を確認します。

SHOW VARIABLES LIKE 'general_log_file';

出力に、先ほど設定したパスが表示されます。

+------------------+-------------------------+
| Variable_name    | Value                   |
+------------------+-------------------------+
| general_log_file | /tmp/mysql_general.log  |
+------------------+-------------------------+
1 row in set (0.01 sec)

次に、ログに記録する操作をいくつか実行します。

CREATE DATABASE IF NOT EXISTS testdb;
USE testdb;
SELECT 'Logging this query' AS message;

これらのコマンドを実行したら、MySQLシェルを終了します。

exit

ターミナルに戻り、ログファイルの内容を表示します。

sudo cat /tmp/mysql_general.log

実行したコマンドのほか、接続情報やタイムスタンプも表示されます。これらが記録されていれば、general query logが正しく動作しています。

/usr/sbin/mariadbd, Version: 10.6.18-MariaDB-0ubuntu0.22.04.1 (Ubuntu 22.04). started with:
Tcp port: 3306  Unix socket: /run/mysqld/mysqld.sock
Time                Id Command  Argument
250728 14:12:46     33 Query    SHOW VARIABLES LIKE 'general_log_file'
250728 14:12:50     33 Query    CREATE DATABASE IF NOT EXISTS testdb
                    33 Query    SELECT DATABASE()
                    33 Init DB  testdb
                    33 Query    show databases
                    33 Query    show tables
                    33 Query    SELECT 'Logging this query' AS message
250728 14:12:56     33 Quit

最後に、作業が終わったらログを無効にするのが適切です。ターミナルから直接、次のコマンドを実行します。

sudo mysql -u root -e "SET GLOBAL general_log = 'OFF';"

これにより、ログファイルが増え続けてサーバーのパフォーマンスに影響するのを防げます。

ストアドプロシージャでエラーを処理する

ストアドプロシージャは、主キー列に重複したデータを挿入しようとするなど、さまざまな理由で失敗することがあります。エラーハンドラーを使うと、エラーを捕捉して適切に対処できるため、プロシージャが異常終了するのを防げます。このステップでは、重複キーエラーを処理するエラーハンドラーを備えたストアドプロシージャを作成します。

まず、MySQLサーバーに接続します。

sudo mysql -u root

testdbデータベースがまだ存在しない場合は作成し、そのデータベースに切り替えます。続けて、productsテーブルを作成します。

CREATE DATABASE IF NOT EXISTS testdb;
USE testdb;

CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(255),
    quantity INT
);

次に、新しい商品を挿入するストアドプロシージャを作成します。このプロシージャでは、重複キーエラー(SQLSTATE 23000)を捕捉するDECLARE HANDLERを使い、独自のメッセージを返します。

DELIMITERコマンドで文の終端記号を;から//に変更します。これにより、プロシージャの本文中のセミコロンが正しく処理されます。

DELIMITER //

CREATE PROCEDURE insert_product(IN p_id INT, IN p_name VARCHAR(255))
BEGIN
    -- Declare an EXIT handler for duplicate key errors
    DECLARE EXIT HANDLER FOR SQLSTATE '23000'
    BEGIN
        SELECT 'Error: Product with this ID already exists.' AS message;
    END;

    -- Attempt to insert the product
    INSERT INTO products (id, name, quantity) VALUES (p_id, p_name, 0);
    SELECT 'Product inserted successfully.' AS message;
END //

DELIMITER ;

作成したプロシージャをテストします。まず、新しい商品を挿入します。

CALL insert_product(1, 'Laptop');

この操作が成功すると、成功メッセージが返されます。

+--------------------------------+
| message                        |
+--------------------------------+
| Product inserted successfully. |
+--------------------------------+
1 row in set (0.00 sec)

次に、同じidの商品を挿入してみます。

CALL insert_product(1, 'Desktop');

今度はエラーハンドラーが実行され、一般的なMySQLエラーの代わりにカスタムエラーメッセージが返されます。

+-----------------------------------------------+
| message                                       |
+-----------------------------------------------+
| Error: Product with this ID already exists.   |
+-----------------------------------------------+
1 row in set (0.00 sec)

このように、エラーハンドラーを使うと、ストアドプロシージャをより使いやすく、堅牢にできます。

SIGNALでカスタムエラーを発生させる

エラーハンドラーはエラーを捕捉しますが、SIGNAL文を使うとエラーを発生させられます。標準のデータベース制約では扱えないビジネスルールを適用する場合に便利です。このステップでは、商品の数量に負の値が入力されるのを防ぐため、SIGNALを使うプロシージャを作成します。

引き続きMySQLシェルを使います。シェルを終了している場合は、再度接続してください。

sudo mysql -u root

testdbデータベースを使用していることを確認します。

USE testdb;

次に、商品の数量を更新するストアドプロシージャを作成します。新しい数量が負の場合、プロシージャはSIGNALでカスタムエラーを発生させます。

DELIMITER //

CREATE PROCEDURE update_quantity(IN p_id INT, IN p_quantity INT)
BEGIN
    -- Check if the quantity is negative
    IF p_quantity < 0 THEN
        -- Raise a custom error
        SIGNAL SQLSTATE '45000'
            SET MESSAGE_TEXT = 'Error: Quantity cannot be negative.';
    END IF;

    -- Update the quantity if the check passes
    UPDATE products SET quantity = p_quantity WHERE id = p_id;
    SELECT 'Quantity updated successfully.' AS message;
END //

DELIMITER ;

ここで、SQLSTATE '45000'はユーザー定義エラーを表す汎用の状態コードです。MESSAGE_TEXTには、クライアントに表示するエラーメッセージを設定します。

プロシージャをテストします。まず、前のステップで作成した「Laptop」の数量を正の値に更新します。

CALL update_quantity(1, 50);

この操作が成功すると、次のように表示されます。

+--------------------------------+
| message                        |
+--------------------------------+
| Quantity updated successfully. |
+--------------------------------+
1 row in set (0.00 sec)

次に、負の値を指定して数量を更新してみます。

CALL update_quantity(1, -10);

この呼び出しではSIGNAL文が実行され、プロシージャはカスタムエラーを返して終了します。

ERROR 1644 (45000): Error: Quantity cannot be negative.

これで、SIGNALを使ってデータベース内に独自のビジネスロジックを適用できることが確認できます。

MySQLのエラーログを確認する

MySQLのエラーログは、サーバーレベルの問題を診断するための主要な情報源です。サーバーの起動・停止、重大なエラー、警告が記録されます。このステップでは、エラーログの設定を確認し、コンテナ環境と従来型の環境でログの扱いがどう異なるかを見ていきます。

引き続きMySQLシェルを使います。まず、log_error変数を確認してエラーログファイルの場所を調べます。

SHOW VARIABLES LIKE 'log_error';

このLabEx VM環境(Dockerコンテナ)では、エラーログのパスが空の値として表示される場合があります。

+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_error     |       |
+---------------+-------+
1 row in set (0.001 sec)

注:このLabEx VMのようなコンテナ環境では、MySQL/MariaDBのエラーログは従来のログファイルではなく、コンテナの標準エラー出力に書き込まれることがよくあります。これは、Dockerコンテナで「12-factor app」の方法論に従う一般的な運用方法です。

存在しないデータベースへのアクセスを試し、エラーの動作を確認します。

USE non_existent_database;

予想どおり、このコマンドはクライアントでエラーになります。

ERROR 1049 (42000): Unknown database 'non_existent_database'

MySQLシェルを終了して、ターミナルに戻ります。

exit

従来型のMySQLをインストールした本番環境では、Ubuntuシステムの場合、通常は/var/log/mysql/error.logにエラーログがあります。従来型のログファイルが存在するかどうか、次のコマンドで確認できます。

sudo ls -la /var/log/mysql/ 2> /dev/null || echo "MySQL log directory not found (normal in containerized environments)"

環境ごとのエラーログの違い:

  • 従来型のインストール:エラーログは/var/log/mysql/error.logのようなファイルに書き込まれます
  • コンテナ環境:エラーは標準出力/標準エラー出力に送られ、コンテナランタイムによって収集されることがよくあります
  • クラウドデータベース:通常、クラウドプロバイダーの管理インターフェースからエラーログにアクセスします

本番環境では、次のようなコマンドを使ってエラーログを定期的に確認します。

  • sudo tail -f /var/log/mysql/error.log(ログをリアルタイムで追跡します)
  • sudo grep -i error /var/log/mysql/error.log(特定のエラーを検索します)

ログの確認は、サーバーの状態を監視し、起動の失敗、テーブルの破損、権限の問題などを調査するうえで欠かせません。

まとめ

この実験では、MySQLのエラー処理とログ記録の基本的な手法を学びました。まずgeneral query logを有効にして設定し、デバッグに役立つSQL文の追跡方法を確認しました。次に、ストアドプロシージャにDECLARE HANDLERを使った堅牢なエラー処理を実装し、特定のエラーに適切に対処できるようにしました。

さらに、SIGNAL文でカスタムエラーを発生させ、明確で具体的なフィードバックを返すことで、ビジネスルールを適用する方法を学びました。最後に、従来型のインストールとDockerコンテナのようなコンテナ環境で異なる、MySQLのエラーログの扱いについて確認しました。

従来型のMySQLではエラーログが/var/log/mysql/error.logのようなファイルに書き込まれる一方、コンテナ環境ではコンテナオーケストレーションプラットフォームとの連携を容易にするため、エラー出力が標準出力/標準エラー出力にリダイレクトされることがよくあります。この違いを理解しておくことは、最新のデプロイ環境で作業するうえで重要です。

これらの手法を身につけることで、信頼性の高いデータベースアプリケーションを構築し、問題を効率よく調査し、さまざまなデプロイ環境でMySQLデータベースの整合性を保つための基礎が身につきます。