MySQL 8.0+需先安装data_masking插件才能使用mask_inner函数,参数顺序为mask_inner(str, left_len, right_len, fill_char),且须配合视图隔离、空值兜底和权限管控方可安全脱敏。

MySQL 8.0+ 必须启用 data_masking 插件才能用 mask_inner
不装插件直接调 mask_inner 会报错 FUNCTION mask_inner does not exist。插件只在 Percona Server for MySQL 或部分企业版中原生支持,官方 MySQL 社区版默认不带。确认是否可用:
SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = 'data_masking';
如果返回空,说明没安装。安装命令是:
INSTALL PLUGIN data_masking SONAME 'data_masking.so';
注意:data_masking.so 文件必须已存在于 MySQL 的 plugin_dir 路径下(可通过 SHOW VARIABLES LIKE 'plugin_dir'; 查看)。社区版用户需自行编译或换用字符串函数方案。
mask_inner 函数参数顺序容易写反
mask_inner 的签名是 mask_inner(str, left_len, right_len, fill_char),不是“保留前几位、后几位、中间填什么”的直觉顺序——left_len 和 right_len 是**保留长度**,不是起始位置。写错会导致脱敏异常,比如:
- 想把
'13912345678'变成'139******78',应写mask_inner('13912345678', 3, 2, '*') - 若误写为
mask_inner('13912345678', 3, 4, '*'),结果是'139****678'(后保留 4 位,不是后 2 位) - 若传入
left_len + right_len >= LENGTH(str),函数直接返回原字符串,不报错也不掩码
生产环境不能直接 SELECT mask_inner(phone) 暴露原始值
插件函数本身不阻止原始字段被查出。如果应用层 SQL 写成 SELECT id, name, phone, mask_inner(phone, 3, 4, '*') FROM users,那 phone 列仍以明文返回,前端或日志可能意外泄露。正确做法是:
- 用视图隔离:创建只暴露脱敏列的视图,禁止查询原字段
CREATE VIEW users_safe AS SELECT id, name, mask_inner(phone, 3, 4, '*') AS phone FROM users;- 给下游账号只授
SELECT权限在该视图上,撤销对基表users的访问 - 避免在应用代码里拼接原始字段和脱敏字段同查
mask_inner 不处理 NULL 或空字符串,得手动兜底
mask_inner(NULL, 3, 4, '*') 返回 NULL,mask_inner('', 3, 4, '*') 返回空字符串,不会抛异常也不会变 '***'。如果手机号字段允许为空,且业务要求统一显示为 '***',必须显式判断:
SELECT COALESCE(NULLIF(mask_inner(phone, 3, 4, '*'), ''), '***') AS phone FROM users;
更稳妥的做法是结合 IF:
SELECT IF(phone IS NULL OR phone = '', '***', mask_inner(phone, 3, 4, '*')) AS phone FROM users;
这点常被忽略,上线后发现空号展示异常,才回头补逻辑。
插件看似一行函数搞定,但权限控制、空值处理、视图封装这些环节漏掉任何一个,都可能让脱敏形同虚设。真正安全的不是函数本身,而是它嵌入整个查询链路的方式。


















