今日议题
背景介绍
用户自定义函数的内联
背景介绍
数据库客户端API
目前我们假设所有的用户逻辑都在客户自己的应用中,然后通过客户端协议如JDBC/ODBC和数据库进行通信获取和存储数据,如下图:
嵌入式数据库逻辑
数据库允许将应用逻辑移植到数据库中减少网络通信的交互次数,这样的好处是高效和可重用。
将应用逻辑->
变为数据库逻辑->
用户自定义函数UDF
用户自定义函数(UDF)就是除了系统函数,内嵌在数据库中应用开发者自己写的函数,它可以输入标量参数,执行一些计算,返回一个结果(包括标量结果或者表结果)。
用户自定义函数UDF的优势
它可以鼓励模块化和代码重用,不同应用可以用同一逻辑实现,对于复杂操作很少的网络交互,某些类型的应用可以非常容易的用UDF表达或者读取。
用户自定义函数UDF的劣势
查询优化器是把UDF当成黑盒,因此,
→ 无法评估它的代价,由于UDF有相关查询在里面,因此很难并行。
→ 一些数据库不支持一个线程中执行带UDF的查询,在查询或者WHERE条件里面的复杂UDF,会强制数据库用迭代方式执行。
→ RBAR = "Row By Agonizing Row",也就是严格程序化编码的结果,而不是基于集合的方式,比如循环中一行行和数据库进行交互。
→ 如果UDF里面调用的查询有隐性的Join而优化器无法得知,那样更糟糕。因为数据库执行UDF里的SQL都是一行一行的方式,所以那些跨多语句的优化方式无法被应用到。
UDF的性能
先看SQL Server做的一个实验
MICROSOFT SQL SERVER UDF的历史
- 2001 – Microsoft支持了TSQL标量UDFs.
- 2008 – 用户开始发现UDF是“恶魔”。
- 2010 – Microsoft发现UDF是“恶魔”。
- 2014 – UDF的去相关研究开始@IIT-B。
- 2015 – Froid项目在MSFT Jim Gray Lab开始。
- 2018 – Froid项目进入SQL Server 2019。
Froid项目
自动把UDF转换成关系型表达式,并且可以被内联为子查询。不需要应用程序员去修改UDF的代码,而是在数据库Rewrite阶段就进行转换,从而避免修改基于代价的优化器。商业数据库已经有能力去把这些规则有效的转换为子查询。
子查询重写
数据库本身就把在Where子句中的嵌套子查询当成一个带参数并返回单值或结果集的函数,因此有两种方法进行优化:
→ 去除相关性或者扁平化重写SQL
→ 分解嵌套子查询并存为临时表
LATERAL连接
一个带lateral的内子查询可以引用相关联表的那些行,从而决定是否将它们返回到最终输出。
→ 允许在FROM子句中加入子查询,而且在查询过程中每次迭代一行会评估该内子查询的有关联关系的表的每一行。
→ 内子查询返回的行可以被加入到与外查询Join完的最终结果集中。
FROID 概述
步骤 #1 – 变换语句
步骤 #2 – UDF分块
步骤 #3 – 合并表达式
步骤 #4 – 内联UDF到查询
步骤 #5 – 优化器优化
额外惊喜的优化
优化前
优化后