MySQL 错误处理与日志记录

MySQLBeginner
立即练习

简介

在本实验中,你将学习 MySQL 错误处理和日志记录的基本技巧。有效管理错误对于构建可靠且易于维护的数据库应用程序至关重要,因为这有助于你诊断问题、了解查询执行情况并确保数据完整性。

首先,启用通用查询日志,记录发送到服务器的所有 SQL 语句。它是强大的调试和审计工具。接下来,在存储过程中使用 DECLARE HANDLER 处理意外错误,避免错误导致过程异常中断。你还将学习使用 SIGNAL 语句创建并触发自定义错误,以强制执行业务规则。最后,你会查看 MySQL 错误日志,其中包含服务器运行情况和严重问题的重要信息。

完成本实验后,你将掌握 MySQL 错误处理和日志记录的基础知识,从而能够构建更可靠的数据库解决方案。

启用并查看通用查询日志

通用查询日志会记录客户端发送到服务器的每条 SQL 语句。它是非常有用的调试和审计工具,但应仅在需要时临时启用,因为它可能影响性能并占用大量磁盘空间。本步骤将启用日志、生成一些活动,并查看日志文件。

首先,在桌面上打开终端。

以 root 用户身份连接 MySQL 服务器。在本实验环境中,你可以使用 sudo 连接,无需输入密码。

sudo mysql -u root

看到 mysql> 提示符后,全局启用通用查询日志。

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 shell。

exit

返回终端后,查看日志文件内容。

sudo cat /tmp/mysql_general.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
);

现在,创建一个用于插入新产品的存储过程。这个过程使用 DECLARE HANDLER 捕获主键重复错误(SQLSTATE 23000),并返回自定义消息。

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 shell 中。如果已经退出,请重新连接。

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 shell 中。首先,查询 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 shell,返回终端。

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 等文件
  • 容器化环境:错误通常会发送到 stdout/stderr,并由容器运行时捕获
  • 云数据库:通常通过云服务提供商的管理界面查看错误日志

在生产环境中,你会定期使用以下命令查看错误日志:

  • sudo tail -f /var/log/mysql/error.log(实时跟踪日志)
  • sudo grep -i error /var/log/mysql/error.log(搜索特定错误)

定期检查错误日志对于监控服务器运行状况,以及排查启动失败、表损坏或权限问题等情况至关重要。

总结

在本实验中,你学习了 MySQL 错误处理和日志记录的基本技巧。首先,你启用并配置了通用查询日志,以跟踪 SQL 语句,这是调试所需的一项重要技能。随后,你在存储过程中使用 DECLARE HANDLER 实现了可靠的错误处理,使存储过程能够妥善处理特定错误。

此外,你还学习了如何使用 SIGNAL 语句触发自定义错误来强制执行业务规则,并提供清晰、具体的反馈。最后,你了解了 MySQL 错误日志的相关概念,包括传统安装与 Docker 等容器化环境在错误日志记录方式上的差异。

你了解到,传统 MySQL 安装会将错误日志写入文件(例如 /var/log/mysql/error.log),而容器化环境通常会将错误输出重定向到 stdout/stderr,以便更好地与容器编排平台集成。了解这一点对于使用现代部署环境至关重要。

掌握这些技巧后,你将更有能力构建可靠的数据库应用程序、有效排查问题,并确保 MySQL 数据库在不同部署场景中的完整性。