行筛选器和列掩码是 Unity 目录访问控制,用于限制用户在查询时可以看到的行和列值。 本页介绍这些控件的 表级 形式,这些控件使用 SQL 直接在单个表上配置,并由表所有者管理。 对于跨多个表的一致行筛选和列掩码,建议使用 ABAC 策略。 它们附加在目录或架构层级,并根据治理标签自动生效。
什么是行筛选器?
行筛选器限制用户可以在表中看到的行。 筛选器是一个 SQL 用户定义的函数(UDF),用于在查询时计算每一行。 从查询结果中排除函数返回的 FALSE 行。 这通常用于行级安全。 例如,可以将用户限制为来自特定区域、部门或帐户的记录。
表级行筛选器使用 ALTER TABLE ... SET ROW FILTER 绑定到单个表,并由表所有者管理。
什么是列掩码?
列掩码控制用户针对特定列看到的值。 掩码是一个 SQL UDF,它采用列值作为输入并返回原始值或掩码版本。 返回类型必须匹配或可强制转换为列的数据类型。 每个字段可以有一个掩码。 列掩码可以将其他列作为输入来根据多个属性改变行为。
表级列掩码使用 ALTER TABLE ... ALTER COLUMN ... SET MASK 绑定到列,并由表所有者管理。 它们仅适用于该表上的该列。
何时改用 ABAC 策略或动态视图
Unity 目录为行级别和列级访问控制提供了两种相关机制:
-
动态视图 在 SQL 视图中包装一个或多个基表,该表筛选行、掩码列或重塑数据,通常由组成员身份函数(例如
is_account_group_member()) 进行封闭。 如果要向无权访问基础表的用户公开特选、转换或联接的数据版本,请使用动态视图。 示例用例包括将事实数据表的编辑切片与分析组共享,或将多个表中的列合并到单个安全层。
| 方法 | 适用于 | 使用 管理 | 最适合用于 |
|---|---|---|---|
| 表级行筛选器和列掩码 | 单个表和列 | 由表所有者或具有 ALTER TABLE 的用户MANAGE |
特定于表的逻辑 |
| ABAC 策略 | 与标记条件匹配的表和列 |
CREATE POLICY 由其所有者或具有 MANAGE 的用户附加到目录、架构或表 |
可自动应用于多个表的集中式规则 |
| 动态视图 | 基于一个或多个基表生成的视图 | 视图定义中的 SQL 逻辑 | 共享精选版或转换后的数据版本 |
如何应用行筛选器和列掩码
通过以下方式之一应用行筛选器和列掩码:
- 每个表的手动分配:通过将 UDF 直接分配给单个表和列来应用筛选器和掩码。 这样可以进行细粒度、针对特定表的控制,但更难扩展和维护。 请参阅 手动应用行筛选器和列掩码。
性能建议
行筛选器和列掩码通过在应用筛选或掩码之前确保用户无法查看基表值来控制数据可见性。 当查询引擎必须在优化之间进行选择并防止来自筛选值或掩码值的信息泄漏时,它始终会做出安全选择,这可能会影响查询性能。 若要最大程度地减少影响,
- 使用简单的 UDF。 表达式性能越少的函数性能越好。 首选简单
CASE表达式而不是映射表或表达式子查询。 - 限制大型表中不同列掩码的数量。 查询时会对每种不同的掩码进行评估。 仅将掩码应用于真正敏感的列,并尽可能重复使用掩码函数。
- 减少 UDF 参数的数目。 Azure Databricks无法优化来自 UDF 参数的列引用,即使查询中未使用这些列。 尽可能使用具有较少参数的 UDF。
- 避免包含过多
AND连接符的行筛选器。 对于给定的用户和表,运行时只能解析出一个唯一的行筛选器,因此常见做法是使用AND组合逻辑。 添加的结点越多,组合筛选器就越有可能包含上面警告的模式之一。 尽可能少使用连接词。 - 使用无法引发错误的确定性表达式。 可以引发错误的表达式(如 ANSI 除法)会阻止 SQL 编译器在查询计划中向下推送操作,因为诸如“除以零”之类的错误可能会在筛选或掩码之前显示有关值的信息。 使用永远不会引发错误的确定性表达式,例如
try_divide。 - 优先使用 SQL,而不是 Python UDF。 Python UDF 的性能低于 SQL,并提供更少的优化机会。 如果必须使用 Python,请将 UDF 标记为
DETERMINISTIC(如果适用)。
数据类型不匹配行为
创建行筛选器或列掩码时,传递给函数的每个表列的数据类型必须与 UDF 中的相应参数类型匹配。 如果类型不匹配(例如 STRING 传递给 INT 参数的列),Databricks 会将列值隐式转换为参数类型,这可能会导致列包含无法转换的值时出现意外行为。
禁用 ANSI 模式(spark.sql.ansi.enabled = false),不可转换的值将无提示转换为 NULL,不会引发错误,UDF 接收 NULL 而不是实际列值。 这可以生成不正确的结果,例如返回所有行的行筛选器,而不是筛选它们,或屏蔽错误值的列掩码。 Databricks 建议启用 ANSI 模式(spark.sql.ansi.enabled = true),这会在类型转换失败时引发错误,使问题立即可见,而不是在失败时无提示返回 NULL。
示例:类型不匹配的行筛选器
考虑一个表,其中包含一个STRING列和一个行筛选器,该筛选器的参数被意外声明为INT而不是STRING。
SET spark.sql.ansi.enabled = false;
CREATE TABLE employees (
id INT,
salary INT,
department STRING
);
INSERT INTO employees VALUES
(91, 200000, null),
(1, 200000, 'exec'),
(2, 50000, 'engineering'),
(3, 150000, 'exec');
-- Bug: parameter type is INT, but the column is STRING
CREATE FUNCTION salary_filter(dept INT) RETURNS BOOLEAN
RETURN dept IS NULL;
ALTER TABLE employees SET ROW FILTER salary_filter ON (department);
查询时,department值'exec'和'engineering'不能转换为INT,因此它们会自动转换为NULL。 因为当输入为true时,筛选器会返回NULL,因此会返回所有行,而不是仅返回department实际为NULL的行。
SELECT * FROM employees;
| id | 工资 | 部门 |
|---|---|---|
| 91 | 200000 | null |
| 1 | 200000 | exec |
| 2 | 50000 | 工程 |
| 3 | 150000 | exec |
正确的 UDF 定义使用 STRING 作为参数类型来匹配列。
CREATE FUNCTION salary_filter(dept STRING) RETURNS BOOLEAN
RETURN dept IS NULL;
使用此修补程序,查询仅返回department为NULL的行。
局限性
- 12.2 LTS 之前的 Databricks Runtime 版本不支持行筛选器或列过滤。 这些运行时安全失败,这意味着如果尝试从这些运行时访问表,则不会返回任何数据。
- 不能将行级别安全性或列掩码应用于视图。
- 不能使用 Iceberg REST 目录或 Unity REST API 访问包含行筛选器或列掩码的表。
- 不支持 Delta Lake API。
- Delta Sharing 提供程序不能共享带有表级行过滤器或列掩码的表。 如果共享所有者不受策略限制,则可以共享基于 ABAC 的行筛选器或列掩码的表。
- Delta Sharing 接收方只能对共享表和外表应用行过滤器和列掩码,而不能对流式表或物化视图应用这些规则。
- 不支持对具有策略的表中文件的基于路径的访问。
-
MERGE语句不支持包含嵌套、聚合、窗口、限制或不确定函数的行筛选器或列掩码策略的表。 - 低于 17.2 的 Databricks Runtime 版本不支持在具有针对分区列定义的行筛选器或列掩码策略的分区表上使用
DELETE、UPDATE和MERGE。 - 不支持与原始策略有循环依赖的行筛选或列屏蔽策略。
- 行筛选器和列掩码不能引用同时具有活动行筛选器或列掩码的表。
- 按时间顺序查看不适用于行级安全性或列掩码。 在 ABAC 配置中,从策略中显式排除的用户仍可以对基础数据运行时间旅行查询。
- 对具有行级安全性或列掩码的表不支持深度克隆和浅克隆。
- 不能在由生成列引用的列上应用列掩码。 请参阅 生成的列和列掩码。
专用访问模式限制
不能从 Databricks Runtime 15.3 或更低版本上的专用访问计算资源访问具有行筛选器或列掩码的表。 如果为无服务器计算启用了工作区,则可以在 Databricks Runtime 15.4 LTS 或更高版本上使用专用访问模式。 但是,在 Databricks Runtime 15.4 到 16.2 上,仅支持读取操作。 写入操作(包括 INSERT、UPDATE 和 DELETE)需要 Databricks Runtime 16.3 或更高版本,并且必须使用支持的模式,例如 MERGE INTO。