平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“一文浅析MySQL中自定义函数应该如何设计”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
在这个场景下,平时写SQL,我们一直在用MySQL内置函数,IFNULL、SUBSTRING、DATE_FORMAT 这些随手就来。但遇到业务规则重复、逻辑固定的场景,很多人会想到写自定义函数。
不过我发现一个现象:很多团队一上来就疯狂写UDF,把复杂业务全部塞到MySQL函数里面,最后上线才发现查询性能雪崩、主从同步异常、问题难以排查。
在这个场景下,自定义函数不是银弹,它有自己的适用边界。这篇就聊聊,到底该怎么设计MySQL自定义函数。
先划一条底线:能在应用层实现的逻辑,尽量不要放在MySQL自定义函数里。
适合写自定义函数的场景:
CASE WHEN。不适合,强烈不建议写自定义函数的场景:
MySQL自定义函数(UDF)本质就是输入若干参数,得到单个值。它不能得到多行、不能得到结果集,这是最基础的限制。
从实现思路看,好的自定义函数,同样输入一定得到同样输出。也就是所谓的DETERMINISTIC。
RAND()、UUID()、读取会话变量。从实现思路看,若你的函数是非确定性的,定义时不要随便加上DETERMINISTIC标记。错误标记会导致主从复制、查询优化器产生意想不到的bug。
很多人忽略这个点:binlog在statement模式下,非确定性函数极易造成主从数据不一致。
函数每被调用一次,就要执行一遍。如果放在WHERE子句,数据库会对每一行执行这个函数。表一旦变大,全表扫描的代价直接拉满。
反面例子:
WHERE my_func(user_id) = 1
这种写法,只要my_func是自定义函数,基本无法采用user_id上的索引。数据库不能借助索引预先计算函数结果。
正确思路:优先在应用层预处理,或者采用生成列+索引方案,而不是在WHERE里套自定义函数。
设计的时候,提前定好输入、输出的数据类型,不要隐式类型转换。
举个轻松例子:手机号脱敏函数。输入varchar,输出固定varchar,对NULL做兜底处理。
DELIMITER //
CREATE FUNCTION mask_phone(phone VARCHAR(20))
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
IF phone IS NULL OR LENGTH(phone) <> 11 THEN
RETURN phone;
END IF;
RETURN CONCAT(SUBSTRING(phone,1,3), '****', SUBSTRING(phone,8));
END //
DELIMITER ;
调用:
SELECT mask_phone('13812345678');
-- 输出:138****5678
理解这一步时,这个就是典型适合自定义函数的场景:逻辑轻松,纯字符串处理,无表查询,多处复用。
biz_mask_phone,避免和内置函数重名。CREATE ROUTINE权限。线上不要给普通业务账号开放新建函数权限。-- 强烈不推荐
CREATE FUNCTION get_status_name(status INT) RETURNS VARCHAR(32)
BEGIN
SELECT name INTO @res FROM status_table WHERE id = status;
RETURN @res;
END
这种写法问题很多:
CASE WHEN,或者应用层字典映射。SELECT * FROM user WHERE mask_phone(phone) = '138****5678';
从实现思路看,phone字段上就算有索引,也走不了。数据库无法借助索引匹配函数计算后的结果。
DETERMINISTIC告诉优化器:相同输入输出固定。如果函数里面用了RAND()、NOW(),就不能标记成确定性函数。
理解这一步时,在statement复制模式下,非确定性函数会有主从不一致风险。线上生产库,建议binlog采用row模式规避这类问题。
实际处理时,若数据库里几十上百个自定义函数,业务逻辑分布在DB和代码两层。排查问题的时候,你不知道逻辑是在Java/Python代码里,还是藏在MySQL函数中。调试、版本管理、灰度发布都会变得很麻烦。
当你想写自定义函数前,能够依次评估下面方案:
MySQL自定义函数的设计核心一句话:实际处理时,只放轻松、无IO、无副作用、纯计算的复用逻辑,绝不把核心业务下沉到数据库。
设计时重点关注这几点:
落到代码里,数据库是存储层,不是业务逻辑层。自定义函数只是一个简化SQL的小工具,不是承载业务的容器。把复杂逻辑留在应用代码里,数据库只管存储与轻松计算,才是更稳妥的架构。
理解这一步时,总的来说,MySQL自定义函数适合结合实际项目边做边理解。先抓住核心思路,再逐步补上细节和边界处理,最后效果会更稳定,也更容易复用。