MySQL 5.7及更早版本需用CONCAT+IFNULL+TRIM+REPLACE等组合实现脱敏,注意NULL处理、格式清洗、长度校验及避免WHERE中使用函数导致索引失效。

MySQL 5.7 及更早版本不支持 REGEXP_REPLACE,必须用 CONCAT + LEFT/RIGHT/SUBSTRING + REPLACE 组合实现脱敏;但要注意 CONCAT 遇 NULL 直接返回 NULL,REPLACE 大小写敏感且全局替换,容易漏脱或误脱。
手机号脱敏:别只截中间四位,得防 NULL 和格式异常
常见写法是 CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4)),但它在以下情况会出问题:
- 手机号字段为
NULL时,整结果变NULL—— 应改用CONCAT(IFNULL(LEFT(phone, 3), ''), '****', IFNULL(RIGHT(phone, 4), ''))或更稳妥的CONCAT_WS('', IFNULL(LEFT(phone, 3), ''), '****', IFNULL(RIGHT(phone, 4), '')) - 手机号含空格、括号或短横线(如
'138-1234-5678'),RIGHT(phone, 4)会取到'5678'后的')'或空格 —— 建议先用REPLACE(REPLACE(REPLACE(phone, '-', ''), ' ', ''), ')', '')清洗再截取 - 长度不是 11 位(如测试数据填了 12345)会导致
RIGHT返回空字符串,脱敏后变成'123****'—— 可加条件判断:IF(CHAR_LENGTH(TRIM(REPLACE(REPLACE(phone, '-', ''), ' ', ''))) = 11, CONCAT(...), phone)
身份证号脱敏:用 REPEAT 动态补星号,别硬写固定长度
身份证可能是 15 位或 18 位(末位还可能是 X),写死 '********' 会错位。正确做法是计算中间需掩码的字符数:
CONCAT(LEFT(id_card, 6), REPEAT('*', CHAR_LENGTH(id_card) - 10), RIGHT(id_card, 4))
说明:
-
CHAR_LENGTH(id_card) - 10确保前后共保留 10 位(前 6 + 后 4),其余全掩码 - 若字段含空格或换行,
TRIM()必须前置:TRIM(BOTH '\r\n ' FROM id_card) - 15 位身份证执行该式会得
REPEAT('*', 1),即只掩 1 位,符合常规脱敏要求;18 位则掩 8 位,逻辑自适应 - 注意:该表达式对
NULL仍敏感,外层需套IFNULL或用CONCAT_WS
REPLACE 在脱敏中慎用:它不是“模糊替换”,而是字面精确匹配
有人想用 REPLACE(name, SUBSTRING(name, 2), '***') 实现姓名脱敏,这是错的 —— REPLACE 不接受函数作为第二个参数,且它只做静态子串替换,无法动态定位。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
真正适合的场景极少,例如统一清理邮箱中的测试域:
REPLACE(email, '@test.com', '@***.com') —— 但要注意:
- 大小写敏感:
'@TEST.COM'不会被替换 - 会重复替换:
'user@test.com@test.com'变成'user@***.com@***.com' - 不能用于模糊脱敏(如“张*”),必须配合
LEFT+CONCAT,例如:CONCAT(LEFT(name, 1), REPEAT('*', CHAR_LENGTH(name) - 1))
WHERE 条件里千万别调用这些函数
所有脱敏操作都只能放在 SELECT 列表,绝不能出现在 WHERE、JOIN ON 或 ORDER BY 中:
-
WHERE CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4)) = '138****1234'→ 全表扫描,索引失效 - 原始字段(如
phone)必须保持可查性;如需按脱敏形态筛选(如“查所有脱敏后以138开头的用户”),应在应用层处理,或建STORED生成列并加索引 - 生成列示例:
ADD COLUMN phone_masked VARCHAR(13) AS (CONCAT(IFNULL(LEFT(phone, 3), ''), '****', IFNULL(RIGHT(phone, 4), ''))) STORED,然后对phone_masked建索引
最易被忽略的一点:脱敏逻辑一旦写进 SQL,就固化在查询里,难以按角色/环境动态切换规则。生产环境建议把脱敏移到应用层,数据库只存原始值和预计算的生成列,兼顾安全、性能与灵活性。










