接上期:第二章 Oracle Database In-Memory 体系结构(IM-2.1)
本篇为IM列存储之体系结构(中)篇
In-Memory 存储单元
IM列存储管理优化存储单元中的数据和元数据,而不是传统的Oracle数据块。
Oracle数据库在 In-Memory Area 中维护存储单元。 下图显示了In-Memory Area和与其交互的数据库进程的概述。 其余章节描述各种存储器组件。
图 2-5 IM列存储:内存和进程体系结构
此部分包含以下主题:
- In-Memory 压缩单元(IMCU) In-Memory 压缩单元(IMCU)是包含用于一个或多个列的数据的压缩的只读存储单元。
- 快照元数据单元(SMU) 快照元数据单元(SMU)包含关联的IMCU的元数据和事务信息。
- In-Memory内存表达式单元(IMEU) In-Memory表达式单元(IMEU)是用于实现In-Memory表达式(IM表达式)和用户定义的虚拟列的存储容器。
In-Memory 压缩单元(IMCU)
In-Memory 压缩单元(IMCU)是包含用于一个或多个列的数据的压缩的只读存储单元。
IMCU类似于表空间范围。 IMCU具有两个部分:一组列压缩单元(CU)和包含诸如IM存储索引的元数据的头。
此部分包含以下主题:
- IMCUs 和 Schema 对象 IM列存储将单个对象(表、分区、物化视图)的数据存储在一组IMCU中。 IMCU存储一个且仅一个对象的列数据。
- 列压缩单元 (CU) 列压缩单元(CU)是IMCU中的单个列的连续存储。 每个IMCU具有一个或多个CU。
- In-Memory 存储索引 每个IMCU头都自动创建和管理其CU的In-Memory存储索引(IM存储索引)。 IM存储索引存储IMCU内所有列的最小值和最大值。
IMCUs 和 Schema 对象
IM列存储将单个对象(表、分区、物化视图)的数据存储在一组IMCU中。 IMCU存储一个且仅一个对象的列数据。
对于指定为 INMEMORY
的对象,INMEMORY
子句中列出的每个列都包含在每个IMCU中。 例如,sh.sales
表有7列,如图 2-6 所示。 以下DDL语句将表指定为 INMEMORY
,这意味着每个 sales
的IMCU都包括这7列的列数据:
ALTER TABLE sh.sales INMEMORY MEMCOMPRESS FOR QUERY LOW;
要将 INMEMORY
属性应用于段中的一部分列,必须在一个DDL语句中将所有列指定为 INMEMORY
,然后发出第二个DDL语句以指定排除的列上的 NO INMEMORY
属性。 例如,以下语句指定 sh.sales
中的3列为 NO INMEMORY
,这意味着表中的其他4列保留其 INMEMORY
属性:
ALTER TABLE sh.sales INMEMORY MEMCOMPRESS FOR QUERY LOW
NO INMEMORY (promo_id, quantity_sold, amount_sold);
下图显示了IM列存储中填充的sh模式中的三个表:customers
、 products
和 sales
。 在本示例中,每个表都有指定 INMEMORY
的不同数目的列。 每个表的IMCU只包括指定列的数据。
图 2-6 列和IMCU
此部分包含以下主题:
- In-Memory 压缩 IM列存储使用针对访问速度而不是存储缩减优化的特殊压缩格式。 列格式允许直接对压缩列执行查询。
- IMCU 和 行 每个IMCU包含表段中的行的子集的所有列值(包括空值)。 行的子集称为颗粒。
In-Memory 压缩
IM列存储使用针对访问速度而不是存储缩减优化的特殊压缩格式。 列格式允许直接对压缩列执行查询。
压缩使扫描和过滤操作能够处理少得多的数据,从而优化查询性能。 Oracle数据库仅在结果集需要数据时解压缩数据。
在IM列存储中应用的压缩与混合列压缩密切相关。 两种技术处理列向量,主要区别是用于IM列存储的列向量针对SIMD向量处理进行优化,而混合列压缩的列向量针对磁盘存储进行优化。
当您启用要填充到IM列存储中的对象时,在 INMEMORY
子句中指定压缩类型:FOR DML
、FOR QUERY
(LOW
或 HIGH
)、FOR CAPACITY
(LOW
或 HIGH
) 或 NONE。
IMCU 和 行
每个IMCU包含表段中的行的子集的所有列值(包括空值)。 行的子集称为颗粒。
给定段的所有IMCU包含大致相同的行数。 Oracle数据库根据数据类型、数据格式和压缩类型自动确定颗粒的大小。 较高的压缩级别导致IMCU中的更多行。
在IMCU和一组数据库块之间存在一对多映射。 如示例 2-2 所示,每个IMCU存储用于不同块集合的列的值。
IMCU中的列不排序。 Oracle数据库按照从磁盘读取的顺序填充它们。
IMCU中的行数决定了IMCU消耗的空间量。 如果目标行数导致IMCU增长超过在1MB池中可用的连续1MB区段的量,则IMCU创建附加区段(块)以保持剩余的列CU。 IMCU始终以1 MB为增量分配空间。
示例 2-2 IMCU和行子集
在此简化示例中,只有 customers
表的以下4列具有 INMEMORY
属性:cust_id
、cust_first_name
、cust_last_name
和 cust_gender
。 表中仅存在5行,存储在2个数据块中。 概念上,第一数据块存储其行如下:
82,Madeline,Li,F;37004,Abel,Embrey,M;1714,Hardy,Gentle,M
第二个数据块按如下所示存储行:
代码语言:javascript复制100439,Uma,Campbell,F;3047,Lucia,Downey,F
假设IMCU 1存储第一数据块的数据。 在这种情况下,该数据块存储中的3行的 cust_id 列值如下所示“垂直”存储在CU内:
代码语言:javascript复制82
37004
1714
IMCU 2存储来自第二数据块的数据。 这两行的 cust_id 列值存储在CU中,如下所示:
代码语言:javascript复制100439
3047
因为 cust_id
值是数据块中每行的第一个值,所以 cust_id
列位于IMCU中的第一个位置。 列始终占据相同的位置,因此Oracle数据库可以通过读取段的IMCU重建行。
列压缩单元 (CU)
列压缩单元(CU)是IMCU中的单个列的连续存储。 每个IMCU具有一个或多个CU。
此部分包含以下主题:
- CU的结构 CU被划分为主体和头部。
- 本地词典(Local Dictionary) 在CU中,本地字典具有不同值的列表及其对应的字典代码。
CU的结构
CU被划分为主体和头部。
每个CU的主体存储包括在IMCU中的行范围的列值。 头包含关于存储在CU体中的值的元数据,例如CU内的最小值和最大值。 它还可以包含本地字典,其是该列中的不同值的排序列表及其对应的字典代码。
下图显示了 sales 表的4个CU的IMCU:prod_id
、cust_id
、time_id
和 channel_id
。 每个CU存储包括在IMCU中的行范围的列值。
图 2-7 IMCU中的CU
CU按rowid顺序存储值。 因此,数据库可以通过将行“拼接”在一起来回答查询。 例如,应用程序发出以下查询:
代码语言:javascript复制SELECT cust_id, time_id, channel_id
FROM sales
WHERE prod_id = 5;
数据库通过对值为5的条目 prod_id
列开始扫描。假设数据库在 prod_id
列中的位置2中找到5。 数据库现在必须找到此行的相应cust_id,time_id和channel_id。 因为CU按rowid顺序存储数据,所以数据库可以在那些列的位置2中找到对应的 cust_id
、time_id
, and channel_id
值。 因此,为了回答查询,数据库必须从 cust_id
、time_id
, and channel_id
列中的位置2提取值,然后将该行拼接在一起以将其返回给最终用户。
本地词典(Local Dictionary)
在CU中,本地字典具有不同值的列表及其对应的字典代码。
本地字典存储列中包含的符号。 下图说明了CU如何在 vehicles 表中存储 name 列。
图 2-8 本地词典
在前面的图中,CU只包含7行。 该CU中的每个不同值(例如 Cadillac
或 Audi)被分配不同的字典代码,诸如对于 Cadillac 为2,对于 Audi 为 0
。 CU存储字典代码而不是原始值。
注:
当数据库对连接组(join group)使用公共字典(common dictionary)时,本地字典包含对公共字典的引用,而不是符号。 例如,不是存储用于 vehicles.name
列的值 Audi
, BWM
和 Cadillac
,而是本地字典存储诸如101,220和66的字典代码。
CU头包含列的最小值和最大值。 在本示例中,最小值为 Audi,最大值为 Cadillac。 本地词典存储不同值的列表:Audi
, BMW
和 Cadillac
。 它们对应的字典代码(0
, 1
和 2
)是隐式的。 每个IMCU中的CU的本地字典独立于其他IMCU中的本地字典。
如果一个查询过滤 Audi 汽车,那么数据库只扫描这个IMCU只有 0
个代码。
In-Memory 存储索引
每个IMCU头都自动创建和管理其CU的In-Memory存储索引(IM存储索引)。 IM存储索引存储IMCU内所有列的最小值和最大值。
例如,sales
填充在IM列存储中。 此表的每个IMCU都有所有列。 sales.prod_id
列存储在每个IMCU内的单独CU中。 IMCU报头具有每个 prod_id
CU(以及其它所有CU)的最小值和最大值。
为了消除不必要的扫描,数据库可以基于SQL过滤谓词执行IMCU修剪。 数据库仅扫描满足查询谓词的IMCU,如下图中的 WHERE prod_id > 14 AND prod_id < 29
示例所示。
图 2-9 列数据的存储索引
快照元数据单元(SMU)
快照元数据单元(SMU)包含关联的IMCU的元数据和事务信息。
此部分包含以下主题:
- IMCU 和 SMU In-Memory Area的列池存储实际数据:IMCU和IMEU。 In-Memory Area中的元数据池存储SMU。
- 事务日志(Transaction Journal) 每个SMU包含一个事务日志。 数据库使用事务日志来使IMCU在事务上保持一致。
IMCU 和 SMU
In-Memory Area 的列池存储实际数据:IMCU和IMEU。 In-Memory Area 中的元数据池存储SMU。
图 2-10 IMCU和SMU
此图显示数据池中的IMCU和元数据池中的SMU。
每个IMCU映射到单独的SMU。 因此,如果列式数据池包含100个IMCU,则元数据池包含100个SMU。 SMU为其关联的IMCU存储多种类型的元数据,包括以下内容:
- 对象号
- 列号
- 映射行的信息
事务日志(Transaction Journal)
每个SMU包含一个事务日志。 数据库使用事务日志来使IMCU在事务上保持一致。
数据库使用缓冲区高速缓存(buffer cache)来处理DML,就像未启用IM列存储一样。 例如,UPDATE 语句可能修改IMCU中的行。 在这种情况下,数据库将已修改行的rowid添加到事务日志,并将其标记为从DML语句的SCN起已过期。 如果查询需要访问该行的新版本,则数据库从数据库缓冲区高速缓存中获取该行。
图2-11事务日志(Transaction Journal)
数据库通过合并列、事务日志(transaction journal)和缓冲区高速缓存(buffer cache)的内容来实现读取一致性。 当IMCU在重新填充期间刷新时,查询可以直接从IMCU访问最新的行。
In-Memory 表达式单元 (IMEU)
In-Memory Expression Unit (IMEU) 是用于实现内存表达式(IM表达式)和用户定义的虚拟列的存储容器。
数据库将物化表达式视为IMCU中的其他列。 从概念上讲,IMEU是其父IMCU的逻辑扩展。 正如IMCU可以包含多个列,IMEU可以包含多个虚拟列。
每个IMEU映射到一个IMCU,映射到相同的行集。 IMEU包含其相关IMCU中包含的数据的表达式结果。 当IMCU被填充时,相关联的IMEU也被填充。
典型的IM表达式涉及一个或多个列,可能具有常量,并且与表中的行具有一对一映射。 例如,employees
表的IMCU包含列为 weekly_salary 的行1-1000。 对于存储在此IMCU中的行,IMEU计算自动检测到的IM表达式 weekly_salary*52
和用户定义的虚拟列 quarterly_salary
定义为 weekly_salary*12
。 IMCU中的第三行向下映射到IMEU中的第三行。
IMEU是特定段的IMCU的逻辑扩展。 默认情况下,IMEU从基段继承 INMEMORY
子句属性,包括Oracle Real Application Clusters(Oracle RAC)属性,如 DISTRIBUTE
和 DUPLICATE
。 您可以选择性地启用或禁用IMEU中存储的虚拟列。 您还可以为不同的列指定压缩级别。
表达式统计存储 (ESS)
表达式统计存储(ESS)是由优化器维护的存储关于表达式求值的统计的存储库。 ESS驻留在SGA中,并且仍保留在磁盘上。
启用IM列存储时,数据库会利用ESS的 In-Memory 表达式(IM表达式)功能。 但是,ESS独立于IM列存储。 ESS是数据库的永久组件,不能禁用。
数据库使用ESS来确定表达式是否“热”(经常访问),并且因此是IM表达式的候选。 在查询的硬解析期间,ESS在 SELECT 列表中查找活动表达式,WHERE 子句、GROUP BY
子句等。
对于每个段,ESS维护表达式统计信息,例如:
- 执行频率
- 评估成本
- 时间戳评估
优化器根据成本和评估的次数,为每个表达式分配一个加权分数。 这些值是近似值而不是精确值。 更活跃的表达式具有更高的分数。 ESS维护最常访问的表达式的内部列表。
使用 DBMS_INMEMORY_ADMIN
包控制IM表达式的行为。 例如,IME_CAPTURE_EXPRESSIONS
过程提示数据库标识并逐渐填充数据库中最热的表达式。 IME_POPULATE_EXPRESSIONS
过程强制数据库立即填充表达式。
ESS信息存储在数据字典中,并在 DBA_EXPRESSION_STATISTICS
视图中显示。 此视图显示优化程序发送到ESS的元数据。 IM表达式在 DBA_IM_EXPRESSIONS
视图中显示为系统生成的虚拟列,前缀为字符串 SYS_IME。
In-Memory 进程架构
响应于查询和DML,服务器进程扫描列数据并更新SMU元数据。 后台进程将磁盘中的行数据填充到IM列存储中。
此部分包含以下主题:
- In-Memory 协调器进程(IMCO) In-Memory协调器进程(IMCO)管理IM列存储的许多任务。 它的主要任务是启动背景填充和列数据的重新填充。
- 空间管理工作进程(Wnnn) 空间管理工作进程(Wnnn)代表IMCO填充或重新填充数据。
In-Memory 协调器进程 (IMCO)
In-Memory 协调器进程(IMCO)管理IM列存储的许多任务。 它的主要任务是启动后台填充和列数据的重新填充。
Population是一种流式处理机制,将行数据转换为列格式,然后压缩它。 IMCO自动启动具有除 NONE
之外的任何优先级的 INMEMORY
对象的填充。 当访问优先级为 NONE
的对象时,IMCO使用空间管理工作进程(Wnnn)进程填充它们。
当IMCO后台进程满足临时阈值时,它还启动IM列存储对象的基于阈值的重新填充。 IMCO可以对具有过期条目但不满足过期阈值的IM列存储中的任何IMCU发起涓流(trickle)重新填充。
涓流重新填充(Trickle repopulation)在后台自动发生。 步骤如下:
- IMCO 唤醒。
- IMCO确定是否需要执行群体任务,包括IMCU中是否存在过时的条目。
- 如果IMCO找到过时的条目,则它触发空间管理工作进程以重新填充IMCU中的这些条目。
- IMCO睡眠两分钟,然后返回到步骤1。
空间管理工作进程(Wnnn)
空间管理工作进程(Wnnn)代表IMCO填充或重新填充数据。
在填充期间,Wnnn进程负责创建IMCU、SMU和IMEU。 创建IMEU时,工作进程执行以下任务:
- 识别人口的虚拟列
- 创建虚拟列值
- 计算每一行的值,将数据转换为列格式,并压缩它
- 向空间层注册对象
- 将IMEU与其对应的IMCU关联
注:
在IMEU创建期间,父IMCU仍可用于查询。
在重新填充期间,Wnnn进程基于现有的IMCU和事务日志创建IMCU的新版本,同时临时保留旧版本。 这种机制称为双缓冲。
数据库可以快速地将IM表达式移入和移出IM列存储。 例如,如果IMCU是在没有IMEU的情况下创建的,则数据库可以稍后添加IMEU,而不强制IMCU经历完全重新填充机制。
INMEMORY_MAX_POPULATE_SERVERS
初始化参数控制可以启动用于填充的工作进程的最大数量。INMEMORY_TRICKLE_REPOPULATE_PERCENT
初始化参数控制工作进程可以执行涓流重新填充(trickle repopulation)的最大时间百分比。
(本章未完,见下篇,IM系列之:第二章:IM 列存储体系结构(IM-2.3))
山东Oracle用户组(Shandong Oracle User Group),简称:SDOUG,是一个充满朝气、年轻的非营利性组织,旨在为济南及周边地区技术爱好者提供一个交流平台。SDOUG会不定期组织线下技术分享活动,促进本地区及周边IT技术的发展、帮助技术爱好者提高自己。分享技术、分享快乐,SDOUG在路上。