@gordon.gan/specflow 1.4.0-beta → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/prompts/approval/database-guidance.md +79 -0
- package/prompts/approval/generate.md +569 -290
- package/skills/database/LICENSE +405 -0
- package/skills/database/ORIGIN.md +6 -0
- package/skills/database/README.md +30 -0
- package/skills/database/elasticsearch/LICENSE.txt +202 -0
- package/skills/database/elasticsearch/SKILL.md +199 -0
- package/skills/database/elasticsearch/examples/01-fulltext-search.md +215 -0
- package/skills/database/elasticsearch/examples/02-aggregation-report.md +206 -0
- package/skills/database/elasticsearch/examples/03-reindex-zero-downtime.md +200 -0
- package/skills/database/elasticsearch/examples/04-cluster-monitoring.md +204 -0
- package/skills/database/elasticsearch/references/01-query-dsl-fulltext.md +162 -0
- package/skills/database/elasticsearch/references/02-query-dsl-term.md +210 -0
- package/skills/database/elasticsearch/references/03-aggregations-metric.md +161 -0
- package/skills/database/elasticsearch/references/04-aggregations-bucket.md +236 -0
- package/skills/database/elasticsearch/references/05-mapping-types.md +134 -0
- package/skills/database/elasticsearch/references/06-analyzers.md +187 -0
- package/skills/database/elasticsearch/references/07-cluster-ops.md +225 -0
- package/skills/database/elasticsearch/references/08-elk-integration.md +170 -0
- package/skills/database/mysql/SKILL.md +195 -0
- package/skills/database/mysql/examples/01-connection-pool.md +75 -0
- package/skills/database/mysql/examples/02-slow-query-optimization.md +98 -0
- package/skills/database/mysql/examples/03-master-slave-setup.md +144 -0
- package/skills/database/mysql/examples/04-backup-strategy.md +212 -0
- package/skills/database/mysql/references/01-functions-string.md +103 -0
- package/skills/database/mysql/references/02-functions-date.md +152 -0
- package/skills/database/mysql/references/03-functions-aggregate-window.md +167 -0
- package/skills/database/mysql/references/04-functions-json.md +129 -0
- package/skills/database/mysql/references/05-sql-ddl-types.md +235 -0
- package/skills/database/mysql/references/06-index-optimization.md +232 -0
- package/skills/database/mysql/references/07-replication-ha.md +213 -0
- package/skills/database/mysql/references/08-backup-restore.md +207 -0
- package/skills/database/mysql/references/09-advanced-features.md +345 -0
- package/skills/database/oracle/LICENSE.txt +202 -0
- package/skills/database/oracle/SKILL.md +238 -0
- package/skills/database/oracle/examples/01-plsql-procedure.md +90 -0
- package/skills/database/oracle/examples/02-awr-analysis.md +99 -0
- package/skills/database/oracle/examples/03-rman-backup.md +108 -0
- package/skills/database/oracle/examples/04-dataguard-setup.md +146 -0
- package/skills/database/oracle/references/01-functions-string.md +91 -0
- package/skills/database/oracle/references/02-functions-date.md +71 -0
- package/skills/database/oracle/references/03-analytic-functions.md +103 -0
- package/skills/database/oracle/references/04-plsql-guide.md +303 -0
- package/skills/database/oracle/references/05-performance-tuning.md +164 -0
- package/skills/database/oracle/references/06-backup-recovery.md +115 -0
- package/skills/database/oracle/references/07-dataguard-rac.md +76 -0
- package/skills/database/oracle/references/08-security.md +170 -0
- package/skills/database/oracle/references/09-sql-syntax.md +152 -0
- package/skills/database/oracle/references/10-features.md +174 -0
- package/skills/database/postgresql/LICENSE.txt +202 -0
- package/skills/database/postgresql/SKILL.md +182 -0
- package/skills/database/postgresql/examples/.gitkeep +0 -0
- package/skills/database/postgresql/examples/01-jsonb-query.md +72 -0
- package/skills/database/postgresql/examples/02-cte-recursive.md +110 -0
- package/skills/database/postgresql/examples/03-performance-tuning.md +114 -0
- package/skills/database/postgresql/examples/04-streaming-replication.md +113 -0
- package/skills/database/postgresql/references/.gitkeep +0 -0
- package/skills/database/postgresql/references/01-functions-string.md +174 -0
- package/skills/database/postgresql/references/02-functions-datetime.md +54 -0
- package/skills/database/postgresql/references/03-functions-aggregate-window.md +142 -0
- package/skills/database/postgresql/references/04-functions-jsonb.md +117 -0
- package/skills/database/postgresql/references/05-fulltext-search.md +109 -0
- package/skills/database/postgresql/references/06-index-types.md +95 -0
- package/skills/database/postgresql/references/07-partition-fdw.md +133 -0
- package/skills/database/postgresql/references/08-replication-backup.md +215 -0
- package/skills/database/redis/LICENSE.txt +202 -0
- package/skills/database/redis/SKILL.md +922 -0
- package/skills/database/redis/examples/01-cache-usage.md +104 -0
- package/skills/database/redis/examples/02-session-storage.md +72 -0
- package/skills/database/redis/examples/03-leaderboard.md +63 -0
- package/skills/database/redis/examples/04-redis-cluster-setup.md +70 -0
- package/skills/database/redis/examples/05-stream-queue.md +65 -0
- package/skills/database/redis/references/command-quick-ref.md +180 -0
- package/skills/database/redis/references/commands-admin-key.md +413 -0
- package/skills/database/redis/references/commands-set-sorted-advanced.md +539 -0
- package/skills/database/redis/references/commands-string-hash-list.md +458 -0
- package/skills/database/redis/references/memory-optimization.md +150 -0
- package/skills/database/redis/references/redis-conf-production.md +139 -0
- package/skills/specflow-approval/SKILL.md +100 -181
- package/templates/approval.md +295 -221
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# 示例: 慢查询优化实战
|
|
2
|
+
|
|
3
|
+
## 场景
|
|
4
|
+
|
|
5
|
+
电商系统中查询"最近一个月下单超过 5 次的 VIP 用户及其总消费金额"的报表越来越慢。
|
|
6
|
+
|
|
7
|
+
## 原始查询
|
|
8
|
+
|
|
9
|
+
```sql
|
|
10
|
+
SELECT
|
|
11
|
+
u.id, u.name, u.email,
|
|
12
|
+
COUNT(o.id) AS order_count,
|
|
13
|
+
SUM(o.amount) AS total_amount
|
|
14
|
+
FROM user u
|
|
15
|
+
LEFT JOIN `order` o ON u.id = o.user_id
|
|
16
|
+
WHERE u.level = 'vip'
|
|
17
|
+
AND o.created_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)
|
|
18
|
+
GROUP BY u.id, u.name, u.email
|
|
19
|
+
HAVING order_count > 5
|
|
20
|
+
ORDER BY total_amount DESC
|
|
21
|
+
LIMIT 100;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
执行时间:**12.3s**(`EXPLAIN` 显示全表扫 user 和 order)
|
|
25
|
+
|
|
26
|
+
## 分析过程
|
|
27
|
+
|
|
28
|
+
### Step 1: EXPLAIN 分析
|
|
29
|
+
|
|
30
|
+
```sql
|
|
31
|
+
EXPLAIN FORMAT=JSON
|
|
32
|
+
SELECT
|
|
33
|
+
u.id, u.name, u.email,
|
|
34
|
+
COUNT(o.id) AS order_count,
|
|
35
|
+
SUM(o.amount) AS total_amount
|
|
36
|
+
FROM user u
|
|
37
|
+
LEFT JOIN `order` o ON u.id = o.user_id
|
|
38
|
+
WHERE u.level = 'vip'
|
|
39
|
+
AND o.created_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)
|
|
40
|
+
GROUP BY u.id, u.name, u.email
|
|
41
|
+
HAVING order_count > 5
|
|
42
|
+
ORDER BY total_amount DESC
|
|
43
|
+
LIMIT 100;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**发现的问题**:
|
|
47
|
+
1. `u.level` 没有索引 → type: ALL,全表扫描 50 万用户
|
|
48
|
+
2. `o.created_at` 没有索引 → type: ALL,全表扫描 500 万订单
|
|
49
|
+
3. `GROUP BY` 和 `ORDER BY` 没有索引覆盖 → Using temporary; Using filesort
|
|
50
|
+
|
|
51
|
+
### Step 2: 添加索引
|
|
52
|
+
|
|
53
|
+
```sql
|
|
54
|
+
-- 1. user 表的 level 查询索引
|
|
55
|
+
ALTER TABLE user ADD INDEX idx_level (level);
|
|
56
|
+
|
|
57
|
+
-- 2. order 表的复合索引(user_id 用于 JOIN,created_at 用于时间过滤)
|
|
58
|
+
ALTER TABLE `order` ADD INDEX idx_user_created (user_id, created_at);
|
|
59
|
+
|
|
60
|
+
-- 3. 覆盖索引(减少回表)
|
|
61
|
+
ALTER TABLE `order` ADD INDEX idx_user_created_amount (user_id, created_at, amount);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Step 3: 优化后 EXPLAIN
|
|
65
|
+
|
|
66
|
+
- `user` 表:type: ref(`idx_level`),rows: 5000(从 50 万降到 5000)
|
|
67
|
+
- `order` 表:type: ref(`idx_user_created`),rows: 每用户约 10 行
|
|
68
|
+
- Extra: 不再有 Using temporary; Using filesort
|
|
69
|
+
|
|
70
|
+
## 优化后结果
|
|
71
|
+
|
|
72
|
+
```sql
|
|
73
|
+
-- 优化后查询
|
|
74
|
+
SELECT
|
|
75
|
+
u.id, u.name, u.email,
|
|
76
|
+
COUNT(o.id) AS order_count,
|
|
77
|
+
SUM(o.amount) AS total_amount
|
|
78
|
+
FROM user u
|
|
79
|
+
INNER JOIN `order` o ON u.id = o.user_id
|
|
80
|
+
WHERE u.level = 'vip'
|
|
81
|
+
AND o.created_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)
|
|
82
|
+
GROUP BY u.id
|
|
83
|
+
HAVING order_count > 5
|
|
84
|
+
ORDER BY total_amount DESC
|
|
85
|
+
LIMIT 100;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
执行时间:**12.3s → 0.08s**(提升约 150 倍)
|
|
89
|
+
|
|
90
|
+
## 优化要点总结
|
|
91
|
+
|
|
92
|
+
| 优化项 | 优化前 | 优化后 | 效果 |
|
|
93
|
+
|--------|--------|--------|------|
|
|
94
|
+
| `level` 索引 | ALL(50万行) | ref(5000行) | 减少 99% 扫描 |
|
|
95
|
+
| `(user_id, created_at)` 复合索引 | ALL(500万行) | ref(平均10行/用户) | 减少 99.99% |
|
|
96
|
+
| `LEFT JOIN` 改为 `INNER JOIN` | 含无订单用户 | 仅含订单用户 | 减少数据处理量 |
|
|
97
|
+
| `GROUP BY u.id`(简化列) | 3 列分组 | 1 列分组(id 唯一) | 减少临时表开销 |
|
|
98
|
+
| 数据范围 | 全表扫描 | 索引范围扫描 | 大幅提高 |
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# 示例: 主从复制搭建
|
|
2
|
+
|
|
3
|
+
## 场景
|
|
4
|
+
|
|
5
|
+
为电商平台搭建一主一从架构,实现读写分离和基本高可用。主库处理 DML(写),从库处理 SELECT(读)。
|
|
6
|
+
|
|
7
|
+
## 环境
|
|
8
|
+
|
|
9
|
+
- Master: 192.168.1.100:3306
|
|
10
|
+
- Slave: 192.168.1.101:3306
|
|
11
|
+
- MySQL 8.0.x
|
|
12
|
+
|
|
13
|
+
## 步骤
|
|
14
|
+
|
|
15
|
+
### Step 1: Master 配置
|
|
16
|
+
|
|
17
|
+
编辑 `/etc/my.cnf`:
|
|
18
|
+
|
|
19
|
+
```ini
|
|
20
|
+
[mysqld]
|
|
21
|
+
server-id = 1
|
|
22
|
+
log_bin = /var/log/mysql/mysql-bin
|
|
23
|
+
binlog_format = ROW
|
|
24
|
+
binlog_expire_logs_seconds = 604800
|
|
25
|
+
sync_binlog = 1
|
|
26
|
+
innodb_flush_log_at_trx_commit = 1
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
重启 MySQL:`systemctl restart mysqld`
|
|
30
|
+
|
|
31
|
+
### Step 2: Master 创建复制用户
|
|
32
|
+
|
|
33
|
+
```sql
|
|
34
|
+
CREATE USER 'replicator'@'192.168.1.101' IDENTIFIED BY 'StrongPassword123!';
|
|
35
|
+
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'192.168.1.101';
|
|
36
|
+
FLUSH PRIVILEGES;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Step 3: 记录 Master 二进制日志位置
|
|
40
|
+
|
|
41
|
+
```sql
|
|
42
|
+
FLUSH TABLES WITH READ LOCK; -- 锁住所有表
|
|
43
|
+
SHOW MASTER STATUS;
|
|
44
|
+
-- 输出:
|
|
45
|
+
-- File: mysql-bin.000042
|
|
46
|
+
-- Position: 841236
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
> **注意**:新开一个终端会话执行 `SHOW MASTER STATUS`,不要在锁会话中执行,否则锁会一直持有。
|
|
50
|
+
|
|
51
|
+
### Step 4: 初始数据同步
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# 在 Master 上导出数据
|
|
55
|
+
mysqldump -u root -p --all-databases --single-transaction --master-data=2 > /tmp/mysql_full.sql
|
|
56
|
+
|
|
57
|
+
# 复制到 Slave
|
|
58
|
+
scp /tmp/mysql_full.sql root@192.168.1.101:/tmp/
|
|
59
|
+
|
|
60
|
+
# 解锁 Master
|
|
61
|
+
UNLOCK TABLES;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Step 5: Slave 配置
|
|
65
|
+
|
|
66
|
+
编辑 `/etc/my.cnf`:
|
|
67
|
+
|
|
68
|
+
```ini
|
|
69
|
+
[mysqld]
|
|
70
|
+
server-id = 2
|
|
71
|
+
relay_log = /var/log/mysql/mysql-relay-bin
|
|
72
|
+
read_only = 1
|
|
73
|
+
log_slave_updates = 0 # 可选:记录从库更新到 binlog
|
|
74
|
+
skip_slave_start = 1 # 防止自动启动复制
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
重启 MySQL:`systemctl restart mysqld`
|
|
78
|
+
|
|
79
|
+
### Step 6: Slave 恢复初始数据
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
mysql -u root -p < /tmp/mysql_full.sql
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Step 7: 配置复制
|
|
86
|
+
|
|
87
|
+
```sql
|
|
88
|
+
CHANGE MASTER TO
|
|
89
|
+
MASTER_HOST = '192.168.1.100',
|
|
90
|
+
MASTER_PORT = 3306,
|
|
91
|
+
MASTER_USER = 'replicator',
|
|
92
|
+
MASTER_PASSWORD = 'StrongPassword123!',
|
|
93
|
+
MASTER_LOG_FILE = 'mysql-bin.000042',
|
|
94
|
+
MASTER_LOG_POS = 841236;
|
|
95
|
+
|
|
96
|
+
START SLAVE;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Step 8: 验证复制
|
|
100
|
+
|
|
101
|
+
```sql
|
|
102
|
+
SHOW SLAVE STATUS\G
|
|
103
|
+
-- Slave_IO_Running: Yes
|
|
104
|
+
-- Slave_SQL_Running: Yes
|
|
105
|
+
-- Seconds_Behind_Master: 0
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Step 9: 测试
|
|
109
|
+
|
|
110
|
+
```sql
|
|
111
|
+
-- Master 上插入测试数据
|
|
112
|
+
INSERT INTO test.replication_test VALUES (1, 'hello');
|
|
113
|
+
|
|
114
|
+
-- Slave 上验证
|
|
115
|
+
SELECT * FROM test.replication_test; -- 应该看到数据
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## 验证脚本
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
#!/bin/bash
|
|
122
|
+
# check_replication.sh
|
|
123
|
+
|
|
124
|
+
IO_STATUS=$(mysql -e "SHOW SLAVE STATUS\G" | grep "Slave_IO_Running" | awk '{print $2}')
|
|
125
|
+
SQL_STATUS=$(mysql -e "SHOW SLAVE STATUS\G" | grep "Slave_SQL_Running" | awk '{print $2}')
|
|
126
|
+
LAG=$(mysql -e "SHOW SLAVE STATUS\G" | grep "Seconds_Behind_Master" | awk '{print $2}')
|
|
127
|
+
|
|
128
|
+
if [ "$IO_STATUS" = "Yes" ] && [ "$SQL_STATUS" = "Yes" ]; then
|
|
129
|
+
echo "Replication OK. Lag: ${LAG}s"
|
|
130
|
+
exit 0
|
|
131
|
+
else
|
|
132
|
+
echo "Replication ERROR!"
|
|
133
|
+
exit 1
|
|
134
|
+
fi
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 常见问题排查
|
|
138
|
+
|
|
139
|
+
| 问题 | 检查 | 解决方案 |
|
|
140
|
+
|------|------|---------|
|
|
141
|
+
| Slave_IO_Running: Connecting | 网络连通性 | `ping 192.168.1.100`,检查防火墙 3306 |
|
|
142
|
+
| Slave_IO_Running: No | 复制用户权限 | 检查 MASTER_USER/MASTER_PASSWORD |
|
|
143
|
+
| 主键冲突 | 初始数据不一致 | `SET GLOBAL sql_slave_skip_counter = 1;` |
|
|
144
|
+
| 复制延迟高 | Slave 性能 | 升级硬件、开启并行复制 |
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# 示例: 生产环境备份策略
|
|
2
|
+
|
|
3
|
+
## 场景
|
|
4
|
+
|
|
5
|
+
日活 10 万用户的电商平台,MySQL 总数据量约 200GB,需要 24×7 运行,无法接受超过 30 分钟的数据丢失。
|
|
6
|
+
|
|
7
|
+
## 备份策略
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
全量备份: 每天 02:00 (XtraBackup 物理备份)
|
|
11
|
+
增量备份: 每 6 小时 (XtraBackup 增量)
|
|
12
|
+
二进制日志: 实时归档 (自动备份到 S3)
|
|
13
|
+
保留周期: 7 天全量 + 30 天增量 + 90 天 Binlog
|
|
14
|
+
异地备份: 同步到阿里云 OSS (跨区域复制)
|
|
15
|
+
恢复演练: 每月一次
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 备份脚本
|
|
19
|
+
|
|
20
|
+
### 全量备份脚本
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
#!/bin/bash
|
|
24
|
+
# /usr/local/bin/backup_full.sh
|
|
25
|
+
|
|
26
|
+
BACKUP_DIR="/data/backup/mysql"
|
|
27
|
+
DATE=$(date +%Y%m%d_%H%M%S)
|
|
28
|
+
FULL_DIR="${BACKUP_DIR}/full/${DATE}"
|
|
29
|
+
MYSQL_USER="backup_user"
|
|
30
|
+
MYSQL_PASS="$(cat /etc/mysql/backup_pass)"
|
|
31
|
+
|
|
32
|
+
# 创建备份目录
|
|
33
|
+
mkdir -p ${FULL_DIR}
|
|
34
|
+
|
|
35
|
+
# 执行 XtraBackup 全量备份
|
|
36
|
+
xtrabackup --backup \
|
|
37
|
+
--user=${MYSQL_USER} \
|
|
38
|
+
--password=${MYSQL_PASS} \
|
|
39
|
+
--target-dir=${FULL_DIR} \
|
|
40
|
+
--compress \
|
|
41
|
+
--compress-threads=4 \
|
|
42
|
+
--parallel=4 2>>/var/log/xtrabackup.log
|
|
43
|
+
|
|
44
|
+
if [ $? -eq 0 ]; then
|
|
45
|
+
echo "[$(date)] Full backup completed: ${FULL_DIR}" >> /var/log/backup.log
|
|
46
|
+
|
|
47
|
+
# 同步到 OSS
|
|
48
|
+
ossutil sync ${FULL_DIR} oss://myapp-backup/mysql/full/${DATE}/ \
|
|
49
|
+
--delete --force 2>>/var/log/oss_backup.log
|
|
50
|
+
|
|
51
|
+
# 清理 7 天前的全量备份
|
|
52
|
+
find ${BACKUP_DIR}/full/ -type d -mtime +7 -exec rm -rf {} \;
|
|
53
|
+
echo "[$(date)] Full backup synced to OSS" >> /var/log/backup.log
|
|
54
|
+
else
|
|
55
|
+
echo "[$(date)] Full backup FAILED!" >> /var/log/backup.log
|
|
56
|
+
curl -X POST -H "Content-Type: application/json" \
|
|
57
|
+
-d '{"msg":"MySQL 全量备份失败"}' \
|
|
58
|
+
https://alert.example.com/notify
|
|
59
|
+
fi
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 增量备份脚本
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
#!/bin/bash
|
|
66
|
+
# /usr/local/bin/backup_inc.sh
|
|
67
|
+
|
|
68
|
+
BACKUP_DIR="/data/backup/mysql"
|
|
69
|
+
DATE=$(date +%Y%m%d_%H%M%S)
|
|
70
|
+
INC_DIR="${BACKUP_DIR}/inc/${DATE}"
|
|
71
|
+
MYSQL_USER="backup_user"
|
|
72
|
+
MYSQL_PASS="$(cat /etc/mysql/backup_pass)"
|
|
73
|
+
LATEST_FULL=$(ls -td ${BACKUP_DIR}/full/*/ | head -1)
|
|
74
|
+
|
|
75
|
+
# 找最近的备份作为增量基准备份
|
|
76
|
+
if [ -z "$(ls -A ${BACKUP_DIR}/inc/ 2>/dev/null)" ]; then
|
|
77
|
+
BASEDIR="${LATEST_FULL}"
|
|
78
|
+
else
|
|
79
|
+
BASEDIR=$(ls -td ${BACKUP_DIR}/inc/*/ | head -1)
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
mkdir -p ${INC_DIR}
|
|
83
|
+
|
|
84
|
+
xtrabackup --backup \
|
|
85
|
+
--user=${MYSQL_USER} \
|
|
86
|
+
--password=${MYSQL_PASS} \
|
|
87
|
+
--target-dir=${INC_DIR} \
|
|
88
|
+
--incremental-basedir=${BASEDIR} \
|
|
89
|
+
--compress \
|
|
90
|
+
--compress-threads=4 \
|
|
91
|
+
--parallel=4 2>>/var/log/xtrabackup.log
|
|
92
|
+
|
|
93
|
+
if [ $? -eq 0 ]; then
|
|
94
|
+
echo "[$(date)] Incremental backup completed" >> /var/log/backup.log
|
|
95
|
+
ossutil sync ${INC_DIR} oss://myapp-backup/mysql/inc/${DATE}/ \
|
|
96
|
+
--delete --force 2>>/var/log/oss_backup.log
|
|
97
|
+
find ${BACKUP_DIR}/inc/ -type d -mtime +30 -exec rm -rf {} \;
|
|
98
|
+
else
|
|
99
|
+
echo "[$(date)] Incremental backup FAILED!" >> /var/log/backup.log
|
|
100
|
+
curl -X POST -H "Content-Type: application/json" \
|
|
101
|
+
-d '{"msg":"MySQL 增量备份失败"}' \
|
|
102
|
+
https://alert.example.com/notify
|
|
103
|
+
fi
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Binlog 实时归档
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
#!/bin/bash
|
|
110
|
+
# /usr/local/bin/archive_binlog.sh
|
|
111
|
+
|
|
112
|
+
BINLOG_DIR="/var/log/mysql"
|
|
113
|
+
ARCHIVE_DIR="/data/backup/binlog"
|
|
114
|
+
FILES=($(ls -1t ${BINLOG_DIR}/mysql-bin.* 2>/dev/null))
|
|
115
|
+
|
|
116
|
+
# 排除当前正在使用的 binlog
|
|
117
|
+
CURRENT=$(mysql -e "SHOW MASTER STATUS\G" | grep File | awk '{print $2}')
|
|
118
|
+
for FILE in "${FILES[@]}"; do
|
|
119
|
+
BASENAME=$(basename $FILE)
|
|
120
|
+
if [ "$BASENAME" != "$CURRENT" ] && [ ! -f "${ARCHIVE_DIR}/${BASENAME}.gz" ]; then
|
|
121
|
+
gzip -c $FILE > ${ARCHIVE_DIR}/${BASENAME}.gz
|
|
122
|
+
ossutil cp ${ARCHIVE_DIR}/${BASENAME}.gz oss://myapp-backup/binlog/
|
|
123
|
+
echo "[$(date)] Archived: ${BASENAME}" >> /var/log/binlog_archive.log
|
|
124
|
+
|
|
125
|
+
# 删除本地归档后的 binlog 释放空间
|
|
126
|
+
mysql -e "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);"
|
|
127
|
+
fi
|
|
128
|
+
done
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## 恢复流程
|
|
132
|
+
|
|
133
|
+
### 完整恢复步骤
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
#!/bin/bash
|
|
137
|
+
# /usr/local/bin/restore_mysql.sh
|
|
138
|
+
|
|
139
|
+
RESTORE_DATE=$1 # 格式: 2024-03-15 10:30:00
|
|
140
|
+
|
|
141
|
+
# 1. 从 OSS 下载最近的全量备份
|
|
142
|
+
ossutil cp -r oss://myapp-backup/mysql/full/latest/ /tmp/restore/full/
|
|
143
|
+
echo "Step 1: Full backup downloaded"
|
|
144
|
+
|
|
145
|
+
# 2. 准备全量备份
|
|
146
|
+
xtrabackup --prepare --target-dir=/tmp/restore/full/ --apply-log-only
|
|
147
|
+
echo "Step 2: Full backup prepared"
|
|
148
|
+
|
|
149
|
+
# 3. 按需合并增量备份
|
|
150
|
+
for inc in $(ossutil ls oss://myapp-backup/mysql/inc/ | sort); do
|
|
151
|
+
ossutil cp -r $inc /tmp/restore/inc/
|
|
152
|
+
xtrabackup --prepare --target-dir=/tmp/restore/full/ \
|
|
153
|
+
--incremental-dir=/tmp/restore/inc/ --apply-log-only
|
|
154
|
+
echo "Step 3: Incremental ${inc} merged"
|
|
155
|
+
done
|
|
156
|
+
|
|
157
|
+
# 4. 最终准备(非 apply-log-only,回滚未提交事务)
|
|
158
|
+
xtrabackup --prepare --target-dir=/tmp/restore/full/
|
|
159
|
+
echo "Step 4: Final prepare done"
|
|
160
|
+
|
|
161
|
+
# 5. 停止 MySQL,替换数据目录
|
|
162
|
+
systemctl stop mysqld
|
|
163
|
+
mv /var/lib/mysql /var/lib/mysql_bak
|
|
164
|
+
xtrabackup --copy-back --target-dir=/tmp/restore/full/
|
|
165
|
+
chown -R mysql:mysql /var/lib/mysql
|
|
166
|
+
echo "Step 5: Data restored"
|
|
167
|
+
|
|
168
|
+
# 6. 启动 MySQL
|
|
169
|
+
systemctl start mysqld
|
|
170
|
+
echo "Step 6: MySQL started"
|
|
171
|
+
|
|
172
|
+
# 7. 回放 Binlog 到指定时间点(PITR)
|
|
173
|
+
mysqlbinlog --stop-datetime="${RESTORE_DATE}" \
|
|
174
|
+
/data/backup/binlog/mysql-bin.* | mysql -u root -p
|
|
175
|
+
echo "Step 7: PITR applied to ${RESTORE_DATE}"
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## 定时作业配置
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# crontab -e
|
|
182
|
+
|
|
183
|
+
# 每天 02:00 全量备份
|
|
184
|
+
0 2 * * * /usr/local/bin/backup_full.sh
|
|
185
|
+
|
|
186
|
+
# 每 6 小时增量备份
|
|
187
|
+
0 */6 * * * /usr/local/bin/backup_inc.sh
|
|
188
|
+
|
|
189
|
+
# 每小时检查并归档 binlog
|
|
190
|
+
0 * * * * /usr/local/bin/archive_binlog.sh
|
|
191
|
+
|
|
192
|
+
# 每天 06:00 检查备份完整性
|
|
193
|
+
0 6 * * * /usr/local/bin/check_backup.sh
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## 恢复演练计划
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
每月第一周周日凌晨 2:00 执行:
|
|
200
|
+
|
|
201
|
+
1. 在测试环境恢复最近的全量备份
|
|
202
|
+
2. 应用增量备份
|
|
203
|
+
3. 执行 PITR 到指定时间点
|
|
204
|
+
4. 验证数据完整性:
|
|
205
|
+
- 检查关键表行数
|
|
206
|
+
- 验证最近订单数据
|
|
207
|
+
- 运行业务自检脚本
|
|
208
|
+
5. 记录恢复耗时,持续优化
|
|
209
|
+
|
|
210
|
+
目标 RTO: < 2 小时
|
|
211
|
+
目标 RPO: < 30 分钟
|
|
212
|
+
```
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# 字符串函数 (String Functions)
|
|
2
|
+
|
|
3
|
+
## 简介
|
|
4
|
+
|
|
5
|
+
MySQL 提供丰富的字符串处理函数,用于字符串拼接、截取、替换、格式化等操作。这些函数在数据清洗、脱敏、报表生成中广泛使用。
|
|
6
|
+
|
|
7
|
+
## 常用函数速查
|
|
8
|
+
|
|
9
|
+
### 拼接与格式化
|
|
10
|
+
|
|
11
|
+
| 函数 | 说明 | 示例 | 结果 |
|
|
12
|
+
|------|------|------|------|
|
|
13
|
+
| `CONCAT(s1, s2, ...)` | 字符串拼接 | `CONCAT(first_name, ' ', last_name)` | 'John Doe' |
|
|
14
|
+
| `CONCAT_WS(sep, s1, s2)` | 带分隔符拼接 | `CONCAT_WS('-', '2024', '01', '15')` | '2024-01-15' |
|
|
15
|
+
| `GROUP_CONCAT(col)` | 分组拼接 | `GROUP_CONCAT(name ORDER BY id SEPARATOR ',')` | 'a,b,c' |
|
|
16
|
+
| `FORMAT(x, d)` | 千分位格式化 | `FORMAT(12345.67, 2)` | '12,345.67' |
|
|
17
|
+
| `LPAD(s, n, pad)` | 左填充 | `LPAD('7', 3, '0')` | '007' |
|
|
18
|
+
| `RPAD(s, n, pad)` | 右填充 | `RPAD('7', 3, '0')` | '700' |
|
|
19
|
+
|
|
20
|
+
### 截取与定位
|
|
21
|
+
|
|
22
|
+
| 函数 | 说明 | 示例 | 结果 |
|
|
23
|
+
|------|------|------|------|
|
|
24
|
+
| `SUBSTRING(s, pos, len)` | 子串 | `SUBSTRING('Hello World', 1, 5)` | 'Hello' |
|
|
25
|
+
| `LEFT(s, n)` | 左截取 | `LEFT('abcde', 3)` | 'abc' |
|
|
26
|
+
| `RIGHT(s, n)` | 右截取 | `RIGHT('abcde', 2)` | 'de' |
|
|
27
|
+
| `LOCATE(sub, s, pos)` | 子串位置 | `LOCATE('is', 'this is test')` | 3 |
|
|
28
|
+
| `INSTR(s, sub)` | 子串位置 | `INSTR('this is test', 'is')` | 3 |
|
|
29
|
+
| `SUBSTRING_INDEX(s, delim, n)` | 按分隔符截取 | `SUBSTRING_INDEX('a,b,c', ',', 2)` | 'a,b' |
|
|
30
|
+
|
|
31
|
+
### 替换与转换
|
|
32
|
+
|
|
33
|
+
| 函数 | 说明 | 示例 | 结果 |
|
|
34
|
+
|------|------|------|------|
|
|
35
|
+
| `REPLACE(s, from, to)` | 替换 | `REPLACE('abc123', '123', '456')` | 'abc456' |
|
|
36
|
+
| `INSERT(s, pos, len, new)` | 插入替换 | `INSERT('phone', 2, 4, '****')` | 'p****e' |
|
|
37
|
+
| `UPPER(s)` / `LOWER(s)` | 大小写转换 | `UPPER('abc')` | 'ABC' |
|
|
38
|
+
| `TRIM(s)` | 去首尾空格 | `TRIM(' abc ')` | 'abc' |
|
|
39
|
+
| `LTRIM(s)` / `RTRIM(s)` | 去左/右空格 | `LTRIM(' abc')` | 'abc' |
|
|
40
|
+
| `REVERSE(s)` | 逆序 | `REVERSE('abc')` | 'cba' |
|
|
41
|
+
| `REPEAT(s, n)` | 重复 | `REPEAT('x', 5)` | 'xxxxx' |
|
|
42
|
+
|
|
43
|
+
### 长度与校验
|
|
44
|
+
|
|
45
|
+
| 函数 | 说明 | 示例 | 结果 |
|
|
46
|
+
|------|------|------|------|
|
|
47
|
+
| `LENGTH(s)` | 字节长度 | `LENGTH('你好')` | 6 (utf8mb4) |
|
|
48
|
+
| `CHAR_LENGTH(s)` | 字符长度 | `CHAR_LENGTH('你好')` | 2 |
|
|
49
|
+
| `BIT_LENGTH(s)` | 位长度 | `BIT_LENGTH('A')` | 8 |
|
|
50
|
+
| `ORD(s)` | 首字符 ASCII | `ORD('A')` | 65 |
|
|
51
|
+
| `ASCII(s)` | 首字符 ASCII | `ASCII('A')` | 65 |
|
|
52
|
+
|
|
53
|
+
## 业务场景
|
|
54
|
+
|
|
55
|
+
### 场景 1: 手机号脱敏
|
|
56
|
+
|
|
57
|
+
```sql
|
|
58
|
+
SELECT
|
|
59
|
+
REPLACE(phone, SUBSTRING(phone, 4, 4), '****') AS masked_phone
|
|
60
|
+
FROM user;
|
|
61
|
+
-- 138****0000
|
|
62
|
+
|
|
63
|
+
-- 更推荐的做法(INSERT 函数)
|
|
64
|
+
SELECT INSERT(phone, 4, 4, '****') AS masked_phone FROM user;
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 场景 2: 商品编号补零
|
|
68
|
+
|
|
69
|
+
```sql
|
|
70
|
+
SELECT CONCAT('PRD', LPAD(id, 5, '0')) AS product_no FROM product;
|
|
71
|
+
-- PRD00001, PRD00002, ...
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 场景 3: 统计每个用户的所有订单号
|
|
75
|
+
|
|
76
|
+
```sql
|
|
77
|
+
SELECT user_id,
|
|
78
|
+
GROUP_CONCAT(order_no ORDER BY created_at SEPARATOR ', ') AS order_list
|
|
79
|
+
FROM `order`
|
|
80
|
+
GROUP BY user_id;
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 场景 4: 检查邮箱格式
|
|
84
|
+
|
|
85
|
+
```sql
|
|
86
|
+
SELECT * FROM user WHERE LOCATE('@', email) = 0;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 场景 5: JSON 字符串提取(旧版本兼容)
|
|
90
|
+
|
|
91
|
+
```sql
|
|
92
|
+
-- 在 MySQL 5.7 之前,JSON 字段用字符串存储时的提取方式
|
|
93
|
+
SELECT
|
|
94
|
+
SUBSTRING_INDEX(SUBSTRING_INDEX(attrs, '"color":"', -1), '"', 2) AS color
|
|
95
|
+
FROM product;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 注意事项
|
|
99
|
+
|
|
100
|
+
- `LENGTH()` 返回**字节数**而非字符数,对于多字节字符集(utf8mb4)一个中文字符占 3-4 字节
|
|
101
|
+
- `CHAR_LENGTH()` 返回**字符数**,处理中文时使用此函数
|
|
102
|
+
- `GROUP_CONCAT` 的结果长度受 `group_concat_max_len` 限制(默认 1024)
|
|
103
|
+
- MySQL 字符串索引默认从 **1** 开始(非 0)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# 日期时间函数 (Date & Time Functions)
|
|
2
|
+
|
|
3
|
+
## 简介
|
|
4
|
+
|
|
5
|
+
MySQL 的日期时间函数用于获取当前时间、提取日期组件、格式化和计算日期差。在报表统计、时间范围查询、过期计算等场景中高频使用。
|
|
6
|
+
|
|
7
|
+
## 常用函数速查
|
|
8
|
+
|
|
9
|
+
### 获取当前日期时间
|
|
10
|
+
|
|
11
|
+
| 函数 | 说明 | 示例结果 |
|
|
12
|
+
|------|------|---------|
|
|
13
|
+
| `NOW()` | 当前日期时间 | '2024-03-15 14:30:00' |
|
|
14
|
+
| `CURDATE()` | 当前日期 | '2024-03-15' |
|
|
15
|
+
| `CURTIME()` | 当前时间 | '14:30:00' |
|
|
16
|
+
| `UTC_DATE()` | UTC 当前日期 | '2024-03-15' |
|
|
17
|
+
| `UTC_TIME()` | UTC 当前时间 | '06:30:00' |
|
|
18
|
+
| `UTC_TIMESTAMP()` | UTC 当前日期时间 | '2024-03-15 06:30:00' |
|
|
19
|
+
| `SYSDATE()` | 函数执行时的当前时间(非语句开始时间) | '2024-03-15 14:30:01' |
|
|
20
|
+
| `CURRENT_TIMESTAMP` | NOW() 的同义词 | '2024-03-15 14:30:00' |
|
|
21
|
+
|
|
22
|
+
### 提取日期/时间组件
|
|
23
|
+
|
|
24
|
+
| 函数 | 说明 | 示例 | 结果 |
|
|
25
|
+
|------|------|------|------|
|
|
26
|
+
| `DATE(expr)` | 提取日期部分 | `DATE(NOW())` | '2024-03-15' |
|
|
27
|
+
| `TIME(expr)` | 提取时间部分 | `TIME(NOW())` | '14:30:00' |
|
|
28
|
+
| `YEAR(date)` | 提取年 | `YEAR('2024-03-15')` | 2024 |
|
|
29
|
+
| `MONTH(date)` | 提取月 | `MONTH('2024-03-15')` | 3 |
|
|
30
|
+
| `DAY(date)` | 提取日 | `DAY('2024-03-15')` | 15 |
|
|
31
|
+
| `HOUR(time)` | 提取时 | `HOUR('14:30:00')` | 14 |
|
|
32
|
+
| `MINUTE(time)` | 提取分 | `MINUTE('14:30:00')` | 30 |
|
|
33
|
+
| `SECOND(time)` | 提取秒 | `SECOND('14:30:00')` | 0 |
|
|
34
|
+
| `EXTRACT(unit FROM date)` | 提取任意部分 | `EXTRACT(MONTH FROM '2024-03-15')` | 3 |
|
|
35
|
+
|
|
36
|
+
### 日期运算
|
|
37
|
+
|
|
38
|
+
| 函数 | 说明 | 示例 | 结果 |
|
|
39
|
+
|------|------|------|------|
|
|
40
|
+
| `DATE_ADD(date, INTERVAL expr unit)` | 日期加法 | `DATE_ADD(NOW(), INTERVAL 7 DAY)` | 7 天后 |
|
|
41
|
+
| `DATE_SUB(date, INTERVAL expr unit)` | 日期减法 | `DATE_SUB(NOW(), INTERVAL 1 MONTH)` | 上月同日 |
|
|
42
|
+
| `DATEDIFF(d1, d2)` | 日期差(天) | `DATEDIFF('2024-03-20', '2024-03-15')` | 5 |
|
|
43
|
+
| `TIMESTAMPDIFF(unit, d1, d2)` | 灵活时间差 | `TIMESTAMPDIFF(HOUR, '2024-01-01', NOW())` | 小时数 |
|
|
44
|
+
| `LAST_DAY(date)` | 月末日期 | `LAST_DAY('2024-02-01')` | '2024-02-29' |
|
|
45
|
+
|
|
46
|
+
**支持的时间单位**:`MICROSECOND`, `SECOND`, `MINUTE`, `HOUR`, `DAY`, `WEEK`, `MONTH`, `QUARTER`, `YEAR`
|
|
47
|
+
|
|
48
|
+
### 格式化与转换
|
|
49
|
+
|
|
50
|
+
| 函数 | 说明 | 示例 | 结果 |
|
|
51
|
+
|------|------|------|------|
|
|
52
|
+
| `DATE_FORMAT(date, fmt)` | 日期格式化 | `DATE_FORMAT(NOW(), '%Y年%m月%d日')` | '2024年03月15日' |
|
|
53
|
+
| `TIME_FORMAT(t, fmt)` | 时间格式化 | `TIME_FORMAT('14:30:00', '%H:%i')` | '14:30' |
|
|
54
|
+
| `STR_TO_DATE(str, fmt)` | 字符串转日期 | `STR_TO_DATE('2024-03-15', '%Y-%m-%d')` | 2024-03-15 |
|
|
55
|
+
| `UNIX_TIMESTAMP([date])` | 转 Unix 时间戳 | `UNIX_TIMESTAMP('2024-03-15')` | 1710489600 |
|
|
56
|
+
| `FROM_UNIXTIME(ts)` | 时间戳转日期 | `FROM_UNIXTIME(1710489600)` | '2024-03-15 00:00:00' |
|
|
57
|
+
|
|
58
|
+
**DATE_FORMAT 常用格式符**:
|
|
59
|
+
|
|
60
|
+
| 格式符 | 说明 | 示例 |
|
|
61
|
+
|--------|------|------|
|
|
62
|
+
| `%Y` | 四位年份 | 2024 |
|
|
63
|
+
| `%y` | 两位年份 | 24 |
|
|
64
|
+
| `%m` | 两位月份 | 03 |
|
|
65
|
+
| `%c` | 月份(无前导零) | 3 |
|
|
66
|
+
| `%d` | 两位日期 | 15 |
|
|
67
|
+
| `%e` | 日期(无前导零) | 15 |
|
|
68
|
+
| `%H` | 24 小时制(00-23) | 14 |
|
|
69
|
+
| `%h` / `%I` | 12 小时制(01-12) | 02 |
|
|
70
|
+
| `%i` | 分钟(00-59) | 30 |
|
|
71
|
+
| `%s` | 秒(00-59) | 00 |
|
|
72
|
+
| `%W` | 星期名称 | Friday |
|
|
73
|
+
| `%M` | 月份名称 | March |
|
|
74
|
+
| `%a` | 缩写星期 | Fri |
|
|
75
|
+
| `%b` | 缩写月份 | Mar |
|
|
76
|
+
|
|
77
|
+
### 星期与周
|
|
78
|
+
|
|
79
|
+
| 函数 | 说明 | 示例 | 结果 |
|
|
80
|
+
|------|------|------|------|
|
|
81
|
+
| `WEEKDAY(date)` | 周索引 (0=Mon, 6=Sun) | `WEEKDAY('2024-03-18')` | 0 (周一) |
|
|
82
|
+
| `DAYOFWEEK(date)` | 周索引 (1=Sun, 7=Sat) | `DAYOFWEEK('2024-03-18')` | 2 (周一) |
|
|
83
|
+
| `DAYNAME(date)` | 星期名 | `DAYNAME('2024-03-18')` | 'Monday' |
|
|
84
|
+
| `MONTHNAME(date)` | 月份名 | `MONTHNAME('2024-03-18')` | 'March' |
|
|
85
|
+
| `WEEK(date[, mode])` | 周数 | `WEEK('2024-03-18')` | 12 |
|
|
86
|
+
| `WEEKOFYEAR(date)` | ISO 周数 | `WEEKOFYEAR('2024-03-18')` | 12 |
|
|
87
|
+
| `QUARTER(date)` | 季度 | `QUARTER('2024-03-18')` | 1 |
|
|
88
|
+
| `DAYOFYEAR(date)` | 一年中的第几天 | `DAYOFYEAR('2024-03-18')` | 78 |
|
|
89
|
+
|
|
90
|
+
## 业务场景
|
|
91
|
+
|
|
92
|
+
### 场景 1: 本月、本周、本日统计
|
|
93
|
+
|
|
94
|
+
```sql
|
|
95
|
+
-- 本月注册用户
|
|
96
|
+
SELECT COUNT(*) FROM user
|
|
97
|
+
WHERE created_at >= DATE_FORMAT(CURDATE(), '%Y-%m-01');
|
|
98
|
+
|
|
99
|
+
-- 本周注册用户(周一为一周开始)
|
|
100
|
+
SELECT COUNT(*) FROM user
|
|
101
|
+
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY);
|
|
102
|
+
|
|
103
|
+
-- 今日统计
|
|
104
|
+
SELECT COUNT(*) FROM `order`
|
|
105
|
+
WHERE DATE(created_at) = CURDATE();
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 场景 2: 按年月聚合
|
|
109
|
+
|
|
110
|
+
```sql
|
|
111
|
+
SELECT
|
|
112
|
+
DATE_FORMAT(created_at, '%Y-%m') AS month,
|
|
113
|
+
COUNT(*) AS order_count,
|
|
114
|
+
SUM(amount) AS total_revenue
|
|
115
|
+
FROM `order`
|
|
116
|
+
GROUP BY month
|
|
117
|
+
ORDER BY month;
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 场景 3: 计算用户注册天数
|
|
121
|
+
|
|
122
|
+
```sql
|
|
123
|
+
SELECT id, name,
|
|
124
|
+
DATEDIFF(NOW(), created_at) AS days_since_reg
|
|
125
|
+
FROM user;
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 场景 4: 计算任务耗时
|
|
129
|
+
|
|
130
|
+
```sql
|
|
131
|
+
SELECT task_id,
|
|
132
|
+
TIMESTAMPDIFF(SECOND, start_time, end_time) AS duration_seconds
|
|
133
|
+
FROM task;
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 场景 5: 上月同期对比
|
|
137
|
+
|
|
138
|
+
```sql
|
|
139
|
+
SELECT
|
|
140
|
+
DATE_FORMAT(created_at, '%Y-%m-%d') AS day,
|
|
141
|
+
COUNT(*) AS orders_today
|
|
142
|
+
FROM `order`
|
|
143
|
+
WHERE created_at >= DATE_SUB(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), INTERVAL WEEKDAY(DATE_SUB(CURDATE(), INTERVAL 1 MONTH)) DAY)
|
|
144
|
+
AND created_at < CURDATE();
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## 注意事项
|
|
148
|
+
|
|
149
|
+
- `DATETIME` vs `TIMESTAMP`:`TIMESTAMP` 会自动时区转换,范围仅到 2038 年
|
|
150
|
+
- `NOW()` 和 `SYSDATE()` 的区别:`NOW()` 返回语句开始的时刻,`SYSDATE()` 返回函数执行时的时刻
|
|
151
|
+
- MySQL 5.6.4+ 支持毫秒精度:`NOW(3)`, `CURTIME(6)`
|
|
152
|
+
- 日期函数中使用 `DATE()` 包裹列会导致索引失效(应改用范围查询)
|