作者:张乐奕
2014 年 12 月发布
Oracle 发布的 Database 12c 最新版本 12.1.0.2 中正式引入了 IMCS(列式内存)功能,正是在刚发布的时候被称为 In-Memory Database Option 的选件。
IMCS 是 SGA 中的一块新的独立区域,就跟众所周知的 Buffer Cache 或者 Shared Pool 一样,IMCS 更接近于 Buffer Cache,就是缓存表或者表分区或者物化视图中的数据的,但是不同的地方是在 IMCS 中缓存的数据格式并不是按照传统的行方式,而是按照列方式来缓存的。更简单地说,现在 Oracle 数据库在内存中既可以缓存行式数据也可以缓存列式数据了,但是在磁盘中存储的始终只有一份行式数据,这是 Oracle IMCS 与 SAP HANA 或者 Sybase IQ 等列式内存数据库截然不同的地方。
Oracle 在磁盘中始终只有一份行式数据,只是在缓存到内存中的时候,可以选择附加一份列式缓存。请注意,这里提及的“附加”一词,意味着,Oracle 数据库以前的所有行为都没有发生变化,应该缓存到 Buffer Cache(传统的行式缓存)中的还是会缓存到 Buffer Cache 中,只是如果设置了某张表也需要一份列式缓存,那么 Oracle 会额外维护一份该表的列式数据,这份数据就是存在 SGA 的新区域 In-Memory Area 中。这份数据只会保存在内存中,一旦数据库实例重启,这部分内存会重新加载(按照不同的设置,可以是一启动就立刻加载,也可以使用到再加载)。
Oracle 需要保证效率的地方是当大量的 DML 产生时,额外的这部分列式缓存如何高效地保持更新和同步,特别是在 RAC 环境中更会是一个挑战,Oracle 并没有透露更多关于这方面的信息,所有一切唯有等待真正应用的测试。
本文主要通过解释跟 IMCS 相关的数据库初始化参数,来介绍其中的知识。包括隐含参数,总共有 115 个参数跟 inmemory 相关 (wow!)。
SQL> @p "inmemory" NAME VALUE ------------------------------------------------------------ ---------------------------------------- _inmemory_check_prot_meta FALSE _inmemory_private_journal_quota 100 _inmemory_private_journal_sharedpool_quota 20 _inmemory_private_journal_numbkts 512 _inmemory_private_journal_numgran 128 _inmemory_jscan 0 _inmemory_pin_hist_mode 16 _inmemory_txn_checksum 0 _inmemory_buffer_waittime 100 _inmemory_cu_timeout 100 _inmemory_cudrop_timeout 1000 _inmemory_exclto_timeout 1000 _inmemory_num_hash_latches 256 _inmemory_strdlxid_timeout 0 _inmemory_incremental_repopulation FALSE _inmemory_lock_for_smucreate FALSE _inmemory_auto_distribute TRUE _inmemory_autodist_2safe FALSE _inmemory_distribute_timeout 300 _inmemory_distribute_ondemand_timeout 300 inmemory_size 218103808 _inmemory_64k_percent 30 _inmemory_min_ima_defersize 0 _inmemory_memprot TRUE _inmemory_analyzer_optimize_for 0 _inmemory_default_flags 8459 _inmemory_default_new FALSE inmemory_clause_default inmemory_force DEFAULT inmemory_query ENABLE _inmemory_query_scan TRUE _inmemory_scan_override FALSE _inmemory_scan_threshold_percent_noscan 50 _inmemory_small_segment_threshold 65536 _inmemory_query_fetch_by_rowid FALSE _inmemory_pruning ON _inmemory_enable_sys FALSE _inmemory_populate_fg FALSE _inmemory_pga_per_server 536870912 inmemory_max_populate_servers 1 _inmemory_servers_throttle_pgalim_percent 55 inmemory_trickle_repopulate_servers_percent 1 _inmemory_populate_wait FALSE _inmemory_populate_wait_max 600 _inmemory_imco_cycle 120 _inmemory_enable_population_verify 1 _inmemory_log_level 1 _inmemory_fs_verify FALSE _inmemory_force_fs FALSE _inmemory_force_fs_tbs SYSAUX _inmemory_force_fs_tbs_size 1073741824 _inmemory_fs_raise_error FALSE _inmemory_fs_nodml FALSE _inmemory_fs_enable FALSE _inmemory_fs_enable_blk_lvl_inv TRUE _inmemory_fs_blk_inv_blkcnt _inmemory_fs_blk_inv_blk_percent 20 _inmemory_enable_stat_alert FALSE _inmemory_imcu_align TRUE _inmemory_max_populate_retry 3 _inmemory_imcu_target_rows 1048576 _inmemory_imcu_target_bytes 0 _inmemory_imcu_source_extents 0 _inmemory_imcu_source_blocks 0 _inmemory_imcu_source_minbytes 1048576 _inmemory_imcu_populate_minbytes 5242880 _inmemory_imcu_source_analyze_bytes 134217728 _inmemory_imcu_target_maxrows 8388608 _inmemory_imcu_source_maxbytes 536870912 _inmemory_max_queued_tasks 208 _inmemory_repopulate_threshold_rows _inmemory_repopulate_threshold_blocks _inmemory_pct_inv_rows_invalidate_imcu 50 _inmemory_pct_inv_blocks_invalidate_imcu 100 _inmemory_repopulate_threshold_mintime_factor 5 _inmemory_repopulate_threshold_mintime 0 _inmemory_repopulate_threshold_scans 0 _inmemory_repopulate_priority_scale_factor 100 _inmemory_repopulate_invalidate_rate_percent 100 _inmemory_repopulate_priority_threshold_row 20 _inmemory_repopulate_priority_threshold_block 40 _inmemory_repopulate_threshold_rows_percent 5 _inmemory_repopulate_threshold_blocks_percent 10 _inmemory_repopulate_disable FALSE _inmemory_check_protect FALSE _inmemory_checksum FALSE _inmemory_validate_fetch FALSE _inmemory_journal_row_logging FALSE _inmemory_journal_check 0 _inmemory_rows_check_interrupt 1000 _inmemory_dbg_scan 0 _inmemory_segment_populate_verify 0 _inmemory_query_check 0 _inmemory_test_verification 0 _inmemory_invalidate_cursors TRUE _inmemory_prepopulate_fg 0 _inmemory_prepopulate TRUE _inmemory_trickle_repopulate TRUE _inmemory_trickle_repopulate_threshold_dirty_ratio 0 _inmemory_trickle_repopulate_min_interval 300 _inmemory_trickle_repopulate_fg 0 _inmemory_force_non_engineered FALSE _inmemory_suppress_vsga_ima FALSE optimizer_inmemory_aware TRUE _optimizer_inmemory_table_expansion TRUE _optimizer_inmemory_gen_pushable_preds TRUE _optimizer_inmemory_autodop TRUE _optimizer_inmemory_access_path TRUE _optimizer_inmemory_quotient 0 _optimizer_inmemory_pruning_ratio_rows 100 _parallel_inmemory_min_time_threshold AUTO _parallel_inmemory_time_unit 1 _optimizer_inmemory_bloom_filter TRUE _optimizer_inmemory_cluster_aware_dop TRUE _optimizer_inmemory_minmax_pruning TRUE 115 ROWS selected.
先将注意力放在非隐含参数上,总共是 7 个参数 (Phew!)。
SQL> SHOW parameter inmemory
NAME TYPE VALUE
-------------------------------------------------- ----------- --------------------------------------------------
inmemory_clause_default string
inmemory_force string DEFAULT
inmemory_max_populate_servers INTEGER 1
inmemory_query string ENABLE
inmemory_size big INTEGER 208M
inmemory_trickle_repopulate_servers_percent INTEGER 1
optimizer_inmemory_aware BOOLEAN TRUE
该参数主要设置 IMCS 的一些默认参数值,这些默认值在今后为新的表或者分区设置 IMCS 时会自动参考。该参数默认值为空,表示除非在建表时明确指定要使用 IMCS,否则不会将表缓存到列式内存中。该参数的设置可选项包括:
NO INMEMORY:与默认值效果一样,除非在建表时明确指定要使用 IMCS,否则不会将表缓存到列式内存中。
INMEMORY:表示默认建表时就会使用 IMCS,除非建表时明确指定“NO INMEMORY”。如果设置为该值,就可以继续设置如下这些其它的 IMCS 参数值。
NO MEMCOMPRESS | MEMCOMPRESS FOR { DML | QUERY [ LOW | HIGH ] | CAPACITY [LOW | HIGH] }:表示在 IMCS 中缓存的列式数据是否需要压缩,如果压缩,采取何种压缩模式。
PRIORITY { NONE | LOW | MEDIUM | HIGH | CRITICAL }:优先级表示 Oracle 将表数据加载到列存中的积极性,Oracle 会最优先考虑 CRITICAL 级别的加载,最后才进行 NONE 级别的加载。要知道内存毕竟是有限的,因此将那些必须要缓存到列存中的表设置为 CRITICAL 是更好的选择。
DISTRIBUTE [ AUTO | BY ROWID RANGE | BY PARTITION | BY SUBPARTITION ]:该参数只对 RAC 环境有效,指定 IMCS 中的数据在 RAC 环境多个节点的内存中如何分布。分别表示自动|按照ROWID范围|按照分区|按照子分区等方式在多个节点内存中缓存数据。
NO DUPLICATE | DUPLICATE [ ALL ]:该参数只对 RAC 环境有效,指定 IMCS 中的数据在 RAC 环境多个节点中是否要镜像。默认为 NO DUPLICATE,也就是一份数据只会在多个节点中维护一份缓存,DUPLICATE 表示维护两份相同的缓存,而 DUPLICATE ALL 则表示在所有节点内存中都维护一份相同的缓存。注意:在非 Oracle 工程系统(比如 Exadata)中,只能是 NO DUPLICATE 效果,即使设置了 DUPLICATE 也不生效。
示例命令:
ALTER system SET
INMEMORY_CLAUSE_DEFAULT='INMEMORY MEMCOMPRESS FOR CAPACITY HIGH'
scope=BOTH;
INMEMORY_FORCE = { DEFAULT | OFF } 当设置为默认值 DEFAULT 时,明确指定了 INMEMORY 的表和物化视图会缓存到 IMCS 中;如果设置为 OFF,则表示全局禁用 IMCS,意味着即使在表和物化视图上指定了 INMEMORY,也不会缓存到 In-Memory Area 中。可以看到,这个参数没有提供全局打开 IMCS 的选项。
该参数定义后台启动的用于将表数据加载至 In-Memory Area 区域中的进程个数,根据文档描述,该参数默认值是 min(一般的 CPU 核数,PGA_AGGREGATE_TARGET/512),当 inmemory_size=0 的时候(也就是没有设置 In-Memory Area 大小的时候),该参数值为 0,一旦设置了inmemory_size,该参数会根据上述算法自动设置。 如果该参数为0,则没有后台进程加载数据,表现为 V$INMEMORY_AREA视图中的POPULATE_STATUS 列始终为 POPULATING。 Oracle 建议该参数值不能设置过高,否则将导致 CPU 资源消耗严重。
该参数用于在 SYSTEM 级别或者 SESSIOn 级别指定是否要启动 inmemory 查询,当设置了 inmemory_size 之后,该值默认会设置未ENABLE。 该参数可以在会话级别设置,临时启用或者禁用 inmemory 查询,用以比较两者之间的性能差距。一旦禁用,则即使相应表已经被缓存到 In-Memory Area 中,Oracle 也不会选择扫描该区域。
该参数用于指定 In-Memory Area 的大小,默认值为 0,意味着默认没有启用 IMCS。如同其他的内存相关参数,修改了此参数以后需要重新启动数据库实例才可以生效。该参数最小需要设置为 100MB。在多租户环境中,如果是在 PDB 级别设置了该参数,则表明该 PDB 可以使用到的最大 IMCS,但是注意的是多个 PDB 的该参数值的总和未必一定要等于在 CDB 级别的设置,甚至可以超越CDB级别的值。如果没有在 PDB 级别设置,那么所有 PDB 都会继承 CDB 上的该参数值,意味着所有 PDB 都可以用到所有可用的 IMCS。 由于 IMCS 是列式存储,并且具有强大的压缩功能,因此即使准备将整个数据库的表都缓存到 IMCS 中,也无需特别大的内存空间。万一内存空间不够,表数据无法缓存到 IMCS 中,在 V$INMEMORY_AREA.POPULATE_STATUS 字段中可以发现“OUT OF MEMORY”字样。 实际上在绝大多数情况下,启用 IMCS 只需要设置这一个初始化参数即可,设置足够大的内存空间,然后尽情地 Cache 那些想缓存的表吧。
该参数定义后台用于重新加载 IMCS 中数据的进程负载,该参数值是一个相对于 inmemory_max_populate_servers 参数值的百分比。举例说,如果 inmemory_max_populate_servers=10,而 inmemory_trickle_repopulate_servers_percent=5,则意味着有 10*0.05=0.5 个进程会负责重新加载。什么是 0.5 个进程,实际上就是重新加载数据的进程负载最多占据一颗 CPU 的 50%。 该参数默认值为 1,在大多数场合都是适用的。
该参数用于限制 Oracle 优化器对于 inmemory 功能的感知,默认值为TRUE,如果将该参数修改为 FALSE,则 Oracle 优化器不会考虑使用 IMCS,等同于设置 OPTIMIZER_FEATURES_ENABLE 小于 12.1.0.2。
鼓励发表数据库选件 (DBO) 相关的内容或在 Oracle 技术网上发表文章。参见在 Oracle 技术网上发表技术文章。