SQL 检测脚本
1、基本介绍
- SQL 检测脚本通常指一段用 SQL 或相关工具编写的脚本,用来自动检查数据库中的某些问题
2、演示
(1)检测目标
- 检查一个电商库的常见问题,假设有张订单表 orders
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
status VARCHAR(20) NOT NULL,
created_at DATETIME NOT NULL
);
(2)检测脚本
- 检查主键重复(数据质量):正常应返回 0 行。有结果说明主键被破坏
SELECT id, COUNT(*) AS cnt
FROM orders
GROUP BY id
HAVING cnt > 1;
- 检查金额异常(业务规则):找出金额为负或异常巨大的订单
SELECT *
FROM orders
WHERE amount <= 0 OR amount > 1000000;
- 检查状态值是否非法(枚举校验):返回空表示所有状态都合法
SELECT DISTINCT status
FROM orders
WHERE status NOT IN ('pending', 'paid', 'shipped', 'done', 'cancelled');
- 检查孤儿数据(外键完整性):找出下了单但用户不存在的脏数据
SELECT o.id, o.user_id
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE u.id IS NULL;
- 检查慢查询 / 长事务(性能):找出执行超过 60 秒的查询
SELECT id, user, time, state, info
FROM information_schema.processlist
WHERE command != 'Sleep'
AND time > 60;
- 检查表空间占用(容量):看哪张表最大,是否异常增长
SELECT
table_name,
ROUND((data_length + index_length) / 1024 / 1024, 2) AS size_mb
FROM information_schema.tables
WHERE table_schema = 'your_db'
ORDER BY size_mb DESC
LIMIT 10;
(3)实际使用
- 实际使用时,通常会把上面这些 SQL 写进一个 SQL 文件,例如,
check_orders.sql
SELECT '主键重复' AS check_item, COUNT(*) AS problem_count
FROM (SELECT id FROM orders GROUP BY id HAVING COUNT(*) > 1) t;
SELECT '金额异常' AS check_item, COUNT(*) AS problem_count
FROM orders WHERE amount <= 0 OR amount > 1000000;
SELECT '状态非法' AS check_item, COUNT(*) AS problem_count
FROM orders
WHERE status NOT IN ('pending','paid','shipped','done','cancelled');
SELECT '孤儿订单' AS check_item, COUNT(*) AS problem_count
FROM orders o LEFT JOIN users u ON o.user_id = u.id
WHERE u.id IS NULL;
mysql -u root -p 【数据库名】 < check_orders.sql
(4)shell 自动跑 + 告警
- 写进一个 shell 脚本,例如,
check_orders.sh
#!/bin/bash
set -euo pipefail
DB_HOST="127.0.0.1"
DB_USER="root"
DB_PASS="your_password"
DB_NAME="your_db"
SQL_FILE="/opt/scripts/check_orders.sql"
LOG_FILE="/var/log/check_orders.log"
ALERT_WEBHOOK="https://your-webhook-url" # 告警接收地址,例如,钉钉、企微
ALERT_THRESHOLD=1 # 问题数达到该值就触发告警,1 表示只要有问题就告警
# 执行检测 SQL,-N 不输出列名,-B 制表符分隔,方便逐行解析
RESULT=$(mysql -h"$DB_HOST" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -N -B < "$SQL_FILE")
ALERT_MSG=""
TOTAL=0
while IFS=$'\t' read -r item count; do
[ -z "$item" ] && continue
echo "$(date '+%F %T') [$item] = $count" >> "$LOG_FILE"
if [ "$count" -ge "$ALERT_THRESHOLD" ]; then
ALERT_MSG="${ALERT_MSG}${item}: ${count} 条\n"
TOTAL=$((TOTAL + count))
fi
done <<< "$RESULT"
# 有问题才发告警,没问题只记日志
if [ "$TOTAL" -gt 0 ]; then
MSG="【订单表检测告警】\n${ALERT_MSG}请及时处理。"
curl -s -X POST "$ALERT_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$MSG\"}}"
echo "$(date '+%F %T') 已发送告警" >> "$LOG_FILE"
else
echo "$(date '+%F %T') 全部正常" >> "$LOG_FILE"
fi
- 赋予可执行权限
chmod +x /opt/scripts/check_orders.sh
- 测试 shell 脚本
/opt/scripts/check_orders.sh
cat /var/log/check_orders.log
- 配置定时任务
crontab -e
0 * * * * /opt/scripts/check_orders.sh >/dev/null 2>&1