DETERMINISTIC函数仅在参数全为常量或查询中不变时可能被MySQL缓存,对表字段参数无效;误标会导致主从不一致、优化器错误合并调用;真正受益的仅是无外部依赖、无状态变更、类型严格匹配的纯计算函数。

DETERMINISTIC函数会被MySQL优化器缓存结果
MySQL在执行SELECT时,如果遇到声明为DETERMINISTIC的函数且参数是常量(比如get_tax_rate(100)),可能直接复用上一次计算结果,跳过函数体执行。但这仅发生在**参数全为常量**或**参数列值在当前查询中未变化**的极少数场景下;一旦参数是表字段(如get_tax_rate(price)),每次行扫描仍会调用函数,缓存基本无效。
- 缓存行为不可控:你无法强制刷新或预热,也不提供命中率监控手段
- 仅对单值计算有效:像
ROUND(x, 2)、CONCAT(a, b)这类纯计算才可能受益 - 查表类函数即使标了
DETERMINISTIC,也不会被缓存——优化器发现内部有SELECT ... INTO就会放弃缓存逻辑
误标DETERMINISTIC反而导致数据不一致
把实际依赖数据库状态的函数(如根据用户ID查最新积分)错误声明为DETERMINISTIC,MySQL不会报错,但会在主从复制或查询重写中出问题:主库返回A,从库因执行时机不同返回B;或者优化器把两次调用合并为一次,后续行直接返回旧值。
- 典型误标:含
SELECT ... FROM users WHERE id = uid的函数 - 隐蔽陷阱:调用了另一个未声明特性的自定义函数,整个链路失去确定性保证
- 时间函数雷区:
NOW()、RAND()、UUID()一出现,就必须用NOT DETERMINISTIC
真正能靠DETERMINISTIC提升效率的函数长什么样
只有同时满足「无外部依赖」「无状态变更」「参数类型与实现完全匹配」的函数,才值得标DETERMINISTIC并可能获得收益。这类函数本质是SQL表达式的封装,不是数据访问入口。
- 输入输出严格映射:例如
CREATE FUNCTION format_phone(n VARCHAR(20)) RETURNS VARCHAR(13) DETERMINISTIC BEGIN RETURN CONCAT('(', LEFT(n,3), ') ', MID(n,4,3), '-', RIGHT(n,4)); END; - 避免隐式转换:参数类型必须和字段类型一致,否则
DETERMINISTIC声明失效(如字段是VARCHAR(32),却用TEXT做参数) - 不能用于索引表达式:哪怕声明了
DETERMINISTIC,若函数体里有子查询或系统变量,MySQL仍拒绝在CREATE INDEX中使用它
比DETERMINISTIC更有效的性能手段
想让查询快,优先考虑绕开函数调用本身。MySQL函数天然不适合处理行级复杂逻辑,它的执行粒度和优化能力远不如原生SQL。
- 把函数逻辑改写进JOIN或子查询:例如把
SELECT name, get_salary_by_dept(dept_id)改成SELECT e.name, d.avg_salary FROM employees e JOIN dept_stats d ON e.dept_id = d.id - 用生成列(GENERATED COLUMN)替代:MySQL 5.7+支持
STORED虚拟列并建索引,比每次调用函数快得多 - 简单规则直接用
CASE WHEN:避免封装成函数,让优化器全程可见、可下推条件
DETERMINISTIC这个标签本身,而是你有没有把函数用在它该在的地方——纯计算、无IO、参数稳定。其他所有场景下,标它只是给未来埋个一致性炸弹。


















