本文目录导读:

我来为您提供监控数据校验失败脚本的编写方案,涵盖基础到进阶的多种场景。
基础数据校验监控脚本
1 文件内容校验监控
#!/bin/bash
# 文件数据校验监控脚本
# 配置变量
LOG_FILE="/var/log/data_validation.log"
NOTIFY_EMAIL="admin@example.com"
THRESHOLD_FAILURE=5 # 允许的最大失败次数
# 日志函数
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> $LOG_FILE
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# 发送告警
send_alert() {
local message=$1
echo "$message" | mail -s "数据校验失败告警" $NOTIFY_EMAIL
# 也可以集成企业微信、钉钉等
# curl -X POST -H "Content-Type: application/json" -d '{"msgtype":"text","text":{"content":"'$message'"}}' YOUR_WEBHOOK_URL
}
# 校验数据格式
validate_data_format() {
local file=$1
local expected_fields=$2
local delimiter=$3
if [ ! -f "$file" ]; then
log_message "ERROR: 文件 $file 不存在"
return 1
fi
local line_num=0
local failed_lines=0
while IFS= read -r line; do
((line_num++))
# 跳过空行和注释行
[ -z "$line" ] && continue
[[ "$line" =~ ^# ]] && continue
# 检查字段数量
fields_count=$(echo "$line" | awk -F"$delimiter" '{print NF}')
if [ "$fields_count" -ne "$expected_fields" ]; then
log_message "ERROR: 第 $line_num 行字段数异常 (期望: $expected_fields, 实际: $fields_count)"
((failed_lines++))
fi
# 检查关键字段不能为空
IFS="$delimiter" read -ra fields <<< "$line"
for i in "${!fields[@]}"; do
if [ -z "${fields[$i]}" ] && [ $i -lt 3 ]; then # 前3个字段不能为空
log_message "ERROR: 第 $line_num 行第 $((i+1)) 个字段为空"
((failed_lines++))
fi
done
done < "$file"
return $failed_lines
}
# 校验数值范围
validate_numeric_range() {
local value=$1
local min=$2
local max=$3
local field_name=$4
if ! [[ "$value" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
log_message "ERROR: $field_name 不是有效数值: $value"
return 1
fi
if (( $(echo "$value < $min" | bc -l) )) || (( $(echo "$value > $max" | bc -l) )); then
log_message "ERROR: $field_name 超出范围 [$min, $max]: $value"
return 1
fi
return 0
}
# 主校验函数
main_validation() {
local data_file="/path/to/data.csv"
local total_failures=0
log_message "开始数据校验..."
# 执行各种校验
validate_data_format "$data_file" 5 ","
if [ $? -gt 0 ]; then
((total_failures+=$?))
fi
# 检查文件完整性
local expected_lines=1000
local actual_lines=$(wc -l < "$data_file")
if [ "$actual_lines" -lt "$expected_lines" ]; then
log_message "ERROR: 数据行数不足 (期望: $expected_lines, 实际: $actual_lines)"
((total_failures++))
fi
# 检查文件更新时间
local file_age=$(stat -c %Y "$data_file")
local current_time=$(date +%s)
local time_diff=$(( (current_time - file_age) / 60 ))
if [ "$time_diff" -gt 60 ]; then # 超过60分钟未更新
log_message "WARNING: 数据文件已 $time_diff 分钟未更新"
((total_failures++))
fi
# 判断是否需要告警
if [ "$total_failures" -gt 0 ]; then
local alert_msg="数据校验失败次数: $total_failures\n请检查日志: $LOG_FILE"
send_alert "$alert_msg"
return 1
fi
log_message "数据校验完成,全部通过"
return 0
}
# 执行主函数
main_validation
exit $?
2 数据库数据校验监控
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
数据库数据校验监控脚本
"""
import pymysql
import logging
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from datetime import datetime, timedelta
import json
class DataValidationMonitor:
def __init__(self, config_file='config.json'):
with open(config_file, 'r') as f:
self.config = json.load(f)
self.setup_logging()
self.failures = []
def setup_logging(self):
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('/var/log/data_validation_monitor.log'),
logging.StreamHandler()
]
)
self.logger = logging.getLogger(__name__)
def connect_db(self):
"""连接数据库"""
try:
connection = pymysql.connect(
host=self.config['db_host'],
user=self.config['db_user'],
password=self.config['db_password'],
database=self.config['db_name'],
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor
)
return connection
except Exception as e:
self.logger.error(f"数据库连接失败: {e}")
return None
# 检查数据完整性
def check_data_integrity(self):
"""检查数据完整性"""
self.logger.info("开始检查数据完整性...")
connection = self.connect_db()
if not connection:
return
try:
with connection.cursor() as cursor:
# 检查主键重复
cursor.execute("""
SELECT table_name, COUNT(*) as duplicate_count
FROM (
SELECT table_name, COUNT(*) as cnt
FROM information_schema.tables
WHERE table_schema = %s
GROUP BY table_name, id
HAVING cnt > 1
) t
GROUP BY table_name
""", (self.config['db_name'],))
duplicates = cursor.fetchall()
if duplicates:
for record in duplicates:
msg = f"表 {record['table_name']} 存在 {record['duplicate_count']} 条重复主键"
self.failures.append(msg)
self.logger.warning(msg)
# 检查外键完整性
cursor.execute("""
SELECT
tc.table_name,
tc.constraint_name
FROM information_schema.table_constraints tc
JOIN information_schema.referential_constraints rc
ON tc.constraint_name = rc.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'
AND tc.table_schema = %s
""", (self.config['db_name'],))
constraints = cursor.fetchall()
for constraint in constraints:
cursor.execute(f"""
SELECT COUNT(*) as orphan_count
FROM {constraint['table_name']}
WHERE {constraint['constraint_name']}_id NOT IN (
SELECT id FROM referenced_table
)
""")
orphan_result = cursor.fetchone()
if orphan_result and orphan_result['orphan_count'] > 0:
msg = f"表 {constraint['table_name']} 存在 {orphan_result['orphan_count']} 条孤立记录"
self.failures.append(msg)
self.logger.warning(msg)
except Exception as e:
self.logger.error(f"数据完整性检查失败: {e}")
self.failures.append(f"数据完整性检查异常: {e}")
finally:
connection.close()
# 检查数据一致性
def check_data_consistency(self):
"""检查跨表数据一致性"""
self.logger.info("开始检查数据一致性...")
connection = self.connect_db()
if not connection:
return
try:
with connection.cursor() as cursor:
# 检查汇总数据一致性
checks = [
{
'name': '订单金额合计',
'sql': """
SELECT
(SELECT COALESCE(SUM(amount), 0) FROM orders) as total_orders,
(SELECT COALESCE(SUM(amount), 0) FROM order_items) as total_items
"""
},
{
'name': '用户统计一致性',
'sql': """
SELECT
(SELECT COUNT(*) FROM users) as user_count,
(SELECT COUNT(DISTINCT user_id) FROM user_profiles) as profile_count
"""
}
]
for check in checks:
cursor.execute(check['sql'])
result = cursor.fetchone()
if result and 'total_orders' in result:
if abs(float(result['total_orders']) - float(result['total_items'])) > 0.01:
msg = f"数据一致性检查失败: {check['name']}"
self.failures.append(msg)
self.logger.warning(f"{msg} - 差异: {abs(float(result['total_orders']) - float(result['total_items']))}")
except Exception as e:
self.logger.error(f"数据一致性检查失败: {e}")
self.failures.append(f"数据一致性检查异常: {e}")
finally:
connection.close()
# 检查数据时效性
def check_data_timeliness(self):
"""检查数据时效性"""
self.logger.info("开始检查数据时效性...")
connection = self.connect_db()
if not connection:
return
try:
with connection.cursor() as cursor:
threshold = datetime.now() - timedelta(hours=1)
cursor.execute("""
SELECT
table_name,
MAX(update_time) as last_update
FROM information_schema.tables
WHERE table_schema = %s
AND table_type = 'BASE TABLE'
GROUP BY table_name
""", (self.config['db_name'],))
tables = cursor.fetchall()
for table in tables:
if table['last_update'] < threshold:
minutes_diff = (datetime.now() - table['last_update']).total_seconds() / 60
msg = f"表 {table['table_name']} 已 {int(minutes_diff)} 分钟未更新"
if minutes_diff > 120: # 超过2小时告警
self.failures.append(msg)
self.logger.warning(msg)
except Exception as e:
self.logger.error(f"数据时效性检查失败: {e}")
finally:
connection.close()
# 发送告警
def send_alert(self):
"""发送告警通知"""
if not self.failures:
return
subject = f"数据校验失败告警 - {len(self.failures)} 个问题"
body = "以下数据校验失败:\n\n"
body += "\n".join([f"- {failure}" for failure in self.failures])
# 邮件告警
if self.config.get('email_enabled'):
try:
msg = MIMEMultipart()
msg['From'] = self.config['email_from']
msg['To'] = ', '.join(self.config['alert_emails'])
msg['Subject'] = subject
msg.attach(MIMEText(body, 'plain', 'utf-8'))
with smtplib.SMTP(self.config['smtp_server'], self.config['smtp_port']) as server:
server.starttls()
server.login(self.config['smtp_user'], self.config['smtp_password'])
server.send_message(msg)
self.logger.info("告警邮件发送成功")
except Exception as e:
self.logger.error(f"告警邮件发送失败: {e}")
# 写入告警日志
with open('/var/log/data_alert.log', 'a') as f:
f.write(f"{datetime.now()} - {subject}\n{body}\n\n")
def run(self):
"""运行所有校验"""
self.logger.info("="*50)
self.logger.info("开始数据校验监控")
self.logger.info("="*50)
# 执行各类校验
self.check_data_integrity()
self.check_data_consistency()
self.check_data_timeliness()
# 发送告警
if self.failures:
self.send_alert()
self.logger.warning(f"发现 {len(self.failures)} 个问题")
else:
self.logger.info("所有校验通过")
self.logger.info("数据校验监控完成")
return len(self.failures)
if __name__ == "__main__":
monitor = DataValidationMonitor('config.json')
exit_code = monitor.run()
exit(exit_code)
高级监控方案
1 带监控看板的完整方案
#!/usr/bin/env python3
"""
完整的API数据校验监控系统
"""
import requests
import time
import json
from statistics import mean, median
from collections import defaultdict
class APIResponseValidator:
def __init__(self, config):
self.config = config
self.metrics = defaultdict(list)
self.failures = []
def validate_response_time(self, url, response):
"""验证响应时间"""
response_time = response.elapsed.total_seconds() * 1000 # ms
self.metrics['response_time'].append(response_time)
if response_time > self.config['max_response_time']:
self.failures.append({
'type': 'response_time',
'url': url,
'value': response_time,
'threshold': self.config['max_response_time']
})
def validate_response_status(self, url, response):
"""验证响应状态码"""
if response.status_code != 200:
self.failures.append({
'type': 'status_code',
'url': url,
'value': response.status_code,
'expected': 200
})
def validate_response_schema(self, url, response, expected_schema):
"""验证响应JSON Schema"""
try:
data = response.json()
# 简洁的模式验证
self._validate_schema_recursive(data, expected_schema, url)
except ValueError:
self.failures.append({
'type': 'invalid_json',
'url': url,
'value': response.text[:100]
})
def _validate_schema_recursive(self, data, schema, url, path=''):
"""递归验证数据结构"""
for key, rules in schema.items():
current_path = f"{path}.{key}" if path else key
if key not in data:
if rules.get('required', False):
self.failures.append({
'type': 'missing_field',
'url': url,
'field': current_path
})
continue
value = data[key]
# 类型检查
if 'type' in rules:
if rules['type'] == 'int' and not isinstance(value, int):
self.failures.append({
'type': 'type_mismatch',
'url': url,
'field': current_path,
'expected': rules['type'],
'actual': type(value).__name__
})
# 范围检查
if 'min' in rules and value < rules['min']:
self.failures.append({
'type': 'value_out_of_range',
'url': url,
'field': current_path,
'value': value,
'min': rules['min']
})
if 'max' in rules and value > rules['max']:
self.failures.append({
'type': 'value_out_of_range',
'url': url,
'field': current_path,
'value': value,
'max': rules['max']
})
# 递归检查子结构
if 'properties' in rules and isinstance(value, dict):
self._validate_schema_recursive(value, rules['properties'], url, current_path)
def validate_data_consistency(self, api_responses):
"""验证多个API之间的数据一致性"""
# 示例:检查不同API返回的用户ID是否一致
user_ids = []
for response in api_responses:
try:
data = response.json()
if 'user_id' in data:
user_ids.append(data['user_id'])
except:
continue
if len(user_ids) > 1 and len(set(user_ids)) != 1:
self.failures.append({
'type': 'data_consistency',
'desc': '不同API返回的用户ID不一致',
'values': user_ids
})
def run_validation(self, endpoints):
"""运行所有验证"""
all_responses = []
for endpoint in endpoints:
try:
response = requests.get(
endpoint['url'],
headers=endpoint.get('headers', {}),
timeout=self.config.get('timeout', 10)
)
all_responses.append(response)
self.validate_response_time(endpoint['url'], response)
self.validate_response_status(endpoint['url'], response)
if 'schema' in endpoint:
self.validate_response_schema(endpoint['url'], response, endpoint['schema'])
except requests.exceptions.RequestException as e:
self.failures.append({
'type': 'request_error',
'url': endpoint['url'],
'error': str(e)
})
return self.failures, self.metrics
# 使用示例
if __name__ == "__main__":
config = {
'max_response_time': 500, # ms
'timeout': 30
}
validator = APIResponseValidator(config)
endpoints = [
{
'url': 'https://api.example.com/users/1',
'headers': {'Authorization': 'Bearer token123'},
'schema': {
'id': {'type': 'int', 'required': True},
'name': {'type': 'str', 'required': True},
'email': {'type': 'str', 'required': True},
'age': {'type': 'int', 'min': 0, 'max': 150}
}
}
]
failures, metrics = validator.run_validation(endpoints)
if failures:
print(f"发现 {len(failures)} 个问题:")
for failure in failures:
print(json.dumps(failure, indent=2))
2 配置文件示例
{
"db_host": "localhost",
"db_user": "monitor",
"db_password": "secure_password",
"db_name": "production_db",
"email_enabled": true,
"email_from": "monitor@example.com",
"alert_emails": ["admin@example.com", "developer@example.com"],
"smtp_server": "smtp.example.com",
"smtp_port": 587,
"smtp_user": "monitor@example.com",
"smtp_password": "smtp_password",
"max_response_time": 500,
"timeout": 30,
"check_interval": 300,
"alert_threshold": 3,
"excluded_tables": ["temp_data", "cache"]
}
自动化部署与执行
1 Cron任务配置
# 每5分钟执行一次数据校验 */5 * * * * /usr/local/bin/data_validation.sh # 每天凌晨2点执行完整性检查 0 2 * * * /usr/local/bin/db_integrity_check.sh # 每30分钟执行API响应验证 */30 * * * * /usr/local/bin/api_validator.py # 每小时检查日志文件 0 * * * * /usr/local/bin/log_validator.sh
2 Systemd服务配置
[Unit] Description=Data Validation Monitor Service After=network.target [Service] Type=simple User=monitor ExecStart=/usr/local/bin/data_validation_monitor.py Restart=on-failure RestartSec=10 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target
最佳实践建议
1 告警级别定义
ALERT_LEVELS = {
'CRITICAL': {
'threshold': 10,
'notify_methods': ['email', 'sms', 'phone'],
'retry_interval': 60 # seconds
},
'WARNING': {
'threshold': 5,
'notify_methods': ['email'],
'retry_interval': 300
},
'INFO': {
'threshold': 1,
'notify_methods': ['log'],
'retry_interval': 3600
}
}
2 故障自动恢复
def auto_retry_with_backoff(func, max_retries=3):
"""带指数退避的重试机制"""
for attempt in range(max_retries):
try:
return func()
except Exception as e:
wait_time = 2 ** attempt
logger.warning(f"第 {attempt + 1} 次重试,等待 {wait_time} 秒")
time.sleep(wait_time)
raise Exception("超过最大重试次数")
这些脚本涵盖了:
- ✅ 文件数据校验
- ✅ 数据库完整性检查
- ✅ API响应验证
- ✅ 数据一致性检查
- ✅ 告警通知机制
- ✅ 自动化部署方案
选择哪种方案取决于您的具体需求:简单文件校验用Shell脚本,复杂业务逻辑用Python,需要实时性则使用API监控方案。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播