@gordon.gan/specflow 1.4.0-beta → 1.4.2-beta

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.
Files changed (82) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/prompts/approval/database-guidance.md +79 -0
  4. package/prompts/approval/generate.md +600 -290
  5. package/skills/database/LICENSE +405 -0
  6. package/skills/database/ORIGIN.md +6 -0
  7. package/skills/database/README.md +30 -0
  8. package/skills/database/elasticsearch/LICENSE.txt +202 -0
  9. package/skills/database/elasticsearch/SKILL.md +199 -0
  10. package/skills/database/elasticsearch/examples/01-fulltext-search.md +215 -0
  11. package/skills/database/elasticsearch/examples/02-aggregation-report.md +206 -0
  12. package/skills/database/elasticsearch/examples/03-reindex-zero-downtime.md +200 -0
  13. package/skills/database/elasticsearch/examples/04-cluster-monitoring.md +204 -0
  14. package/skills/database/elasticsearch/references/01-query-dsl-fulltext.md +162 -0
  15. package/skills/database/elasticsearch/references/02-query-dsl-term.md +210 -0
  16. package/skills/database/elasticsearch/references/03-aggregations-metric.md +161 -0
  17. package/skills/database/elasticsearch/references/04-aggregations-bucket.md +236 -0
  18. package/skills/database/elasticsearch/references/05-mapping-types.md +134 -0
  19. package/skills/database/elasticsearch/references/06-analyzers.md +187 -0
  20. package/skills/database/elasticsearch/references/07-cluster-ops.md +225 -0
  21. package/skills/database/elasticsearch/references/08-elk-integration.md +170 -0
  22. package/skills/database/mysql/SKILL.md +195 -0
  23. package/skills/database/mysql/examples/01-connection-pool.md +75 -0
  24. package/skills/database/mysql/examples/02-slow-query-optimization.md +98 -0
  25. package/skills/database/mysql/examples/03-master-slave-setup.md +144 -0
  26. package/skills/database/mysql/examples/04-backup-strategy.md +212 -0
  27. package/skills/database/mysql/references/01-functions-string.md +103 -0
  28. package/skills/database/mysql/references/02-functions-date.md +152 -0
  29. package/skills/database/mysql/references/03-functions-aggregate-window.md +167 -0
  30. package/skills/database/mysql/references/04-functions-json.md +129 -0
  31. package/skills/database/mysql/references/05-sql-ddl-types.md +235 -0
  32. package/skills/database/mysql/references/06-index-optimization.md +232 -0
  33. package/skills/database/mysql/references/07-replication-ha.md +213 -0
  34. package/skills/database/mysql/references/08-backup-restore.md +207 -0
  35. package/skills/database/mysql/references/09-advanced-features.md +345 -0
  36. package/skills/database/oracle/LICENSE.txt +202 -0
  37. package/skills/database/oracle/SKILL.md +238 -0
  38. package/skills/database/oracle/examples/01-plsql-procedure.md +90 -0
  39. package/skills/database/oracle/examples/02-awr-analysis.md +99 -0
  40. package/skills/database/oracle/examples/03-rman-backup.md +108 -0
  41. package/skills/database/oracle/examples/04-dataguard-setup.md +146 -0
  42. package/skills/database/oracle/references/01-functions-string.md +91 -0
  43. package/skills/database/oracle/references/02-functions-date.md +71 -0
  44. package/skills/database/oracle/references/03-analytic-functions.md +103 -0
  45. package/skills/database/oracle/references/04-plsql-guide.md +303 -0
  46. package/skills/database/oracle/references/05-performance-tuning.md +164 -0
  47. package/skills/database/oracle/references/06-backup-recovery.md +115 -0
  48. package/skills/database/oracle/references/07-dataguard-rac.md +76 -0
  49. package/skills/database/oracle/references/08-security.md +170 -0
  50. package/skills/database/oracle/references/09-sql-syntax.md +152 -0
  51. package/skills/database/oracle/references/10-features.md +174 -0
  52. package/skills/database/postgresql/LICENSE.txt +202 -0
  53. package/skills/database/postgresql/SKILL.md +182 -0
  54. package/skills/database/postgresql/examples/.gitkeep +0 -0
  55. package/skills/database/postgresql/examples/01-jsonb-query.md +72 -0
  56. package/skills/database/postgresql/examples/02-cte-recursive.md +110 -0
  57. package/skills/database/postgresql/examples/03-performance-tuning.md +114 -0
  58. package/skills/database/postgresql/examples/04-streaming-replication.md +113 -0
  59. package/skills/database/postgresql/references/.gitkeep +0 -0
  60. package/skills/database/postgresql/references/01-functions-string.md +174 -0
  61. package/skills/database/postgresql/references/02-functions-datetime.md +54 -0
  62. package/skills/database/postgresql/references/03-functions-aggregate-window.md +142 -0
  63. package/skills/database/postgresql/references/04-functions-jsonb.md +117 -0
  64. package/skills/database/postgresql/references/05-fulltext-search.md +109 -0
  65. package/skills/database/postgresql/references/06-index-types.md +95 -0
  66. package/skills/database/postgresql/references/07-partition-fdw.md +133 -0
  67. package/skills/database/postgresql/references/08-replication-backup.md +215 -0
  68. package/skills/database/redis/LICENSE.txt +202 -0
  69. package/skills/database/redis/SKILL.md +922 -0
  70. package/skills/database/redis/examples/01-cache-usage.md +104 -0
  71. package/skills/database/redis/examples/02-session-storage.md +72 -0
  72. package/skills/database/redis/examples/03-leaderboard.md +63 -0
  73. package/skills/database/redis/examples/04-redis-cluster-setup.md +70 -0
  74. package/skills/database/redis/examples/05-stream-queue.md +65 -0
  75. package/skills/database/redis/references/command-quick-ref.md +180 -0
  76. package/skills/database/redis/references/commands-admin-key.md +413 -0
  77. package/skills/database/redis/references/commands-set-sorted-advanced.md +539 -0
  78. package/skills/database/redis/references/commands-string-hash-list.md +458 -0
  79. package/skills/database/redis/references/memory-optimization.md +150 -0
  80. package/skills/database/redis/references/redis-conf-production.md +139 -0
  81. package/skills/specflow-approval/SKILL.md +102 -181
  82. package/templates/approval.md +321 -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()` 包裹列会导致索引失效(应改用范围查询)