UUID 作为主键(慢插入)

Posted

技术标签:

【中文标题】UUID 作为主键(慢插入)【英文标题】:UUID as primary key (slow inserts) 【发布时间】:2013-12-10 07:32:14 【问题描述】:

我使用 UUID(36 个字符)作为表中的主键。 由于我们达到了大约 150mio 记录,因此插入变得非常慢(似乎是由于 PK 索引)。

有什么办法可以改善这种情况吗?或者你们有什么想法吗?

详情:

1.5 亿行的平均行长。

总共大约 60 个字符(10 列,8 个只是 ID)

慢的定义

每秒 800-1000 行

在加载了多少行之后,它在什么时候变慢了。

afaik 第一个

你是如何加载的(SQL*Loader?SQL?自己的代码?线程?)

Informatica Powercenter

准确的 Oracle 版本(从 v$version 中选择 *)

Oracle 数据库 11g 企业版版本 11.2.0.3.0 - 64 位生产

【问题讨论】:

@MikeW, INSERT DELAYEDmysql 独有的功能 - 但这个问题被标记为 Oracle 是什么让你怀疑PK指数?您是否检查过一个会话的跟踪文件,该会话执行了大量插入操作? 您是否使用 Powercenter 调用 Oracle 的直接路径插入,如果您认为您是,您是否检查了日志以确认您是?我记得有大约 3 种不小心不使用它的不同方式。 嗨@user2428207 这些答案是否有助于解决您的问题?如果不是,请在您的问题中添加不成功的内容。当某个答案对您有所帮助时,您是否可以通过单击旁边的空心绿色复选标记来接受它? 【参考方案1】:

首先:每秒 800-1000 行 - 不是那么慢,尤其是来自外部源。我猜你一个接一个地插入行,Oracle 每次都会解析它,然后每次插入之间都有延迟。

关于提高性能:

A.看看你的表上是否有更多索引。

特别是寻找位图索引,因为它们很难重建。

您还可以轻松检查主键是否真的有问题:

alter table <table> drop constraint <primary key>;

B.看看这个表上是否有“物化视图日志”+“提交时物化视图刷新”。这也会很痛。

如果您想获得最快的性能,根本不要在表上使用索引,而是创建第二个在影子中刷新的索引。也许,为此使用物化视图是有道理的。

【讨论】:

数据来自另一个表。当我开始填充该表时,我有大约 10-12k 次插入/秒 尝试删除PK,你会直接看到原因......你总是可以把它变回来 如果数据来自另一个表 - 为什么需要主键?对于“select”语句,PK 几乎是无用的,因为它只包含有关唯一命中的信息,因此比“TABLE ACCESS FULL”慢 我需要 PK 以确保此 UUID 是唯一的。数据来自的表只是从平面文件临时加载数据的暂存区域。 我刚刚检查过了。似乎有一个 PK 约束(唯一)和一个唯一索引。两者都用有意义吗?【参考方案2】:

36 个字符的主键不应该成为真正减慢插入行的问题。使用具有唯一值的索引强制执行主键。 36 个字符加上一些开销加上使用 AL32UTF8 时的额外内容,仍然允许 8 KB 块大小在一个索引叶块中容纳许多值。

可能还有其他事情发生。

请定义:

150 M 行的平均行长。 慢的定义 在加载多少行之后,它在什么时候变慢。 如何加载(SQL*Loader?SQL?自己的代码?线程?) 准确的 Oracle 版本(从 v$version 中选择 *)

删除索引有帮助

从补充中我了解到,在唯一指标(PK指标)下降后,性能是合理的(22K/s)。

您可能需要检查以下内容:

检查 PowerCenter 正在使用什么后端,这取决于 ETL 作业、版本和平台的可能性。也许是插入/选择,并行是/否,甚至是提取和重新加载。为简单起见,我们假设它是一些并行选择和插入。 11.2.0.3 是一个很好的 Oracle 版本,几乎没有令人讨厌的错误。 根据 DBA 配置空间管理的方式,查看 PK 索引的 initrans 和 maxtrans。第一个确定允许活动的事务数。例如,将 initrans 设置为并发插入的数量。 检查数据字典上的统计信息是否完全不存在或最新(在 dba_tables 中查找实例)。 检查您自己的表上的统计信息是否缺失或相当准确(更改表 xxx 计算所有索引的所有列的表的统计信息)。在加载的数据量和/或分布发生重大变化后重复此步骤。 有时,尤其是在初始加载时,索引可能会变得过于碎片化。也许为 UUID 生成的值对此有所贡献。使用查询可以检测到这一点,但要保持简单:始终在初始加载表后重建索引。随着时间的推移,变化变得越来越小,从 1 行变为 1.000.000 是一个很大的变化,从 100 万变为 200 万则不是。您现在应该只需要这样做一次。

附带说明:以 36 个字符的列作为主键的一行 60 个字符似乎不切实际。每个 ID 大约需要精度 / 2 + 1 个字节(BCD 格式)。但即使行大小翻倍,也不会产生重大影响。

检测受益于重建的索引

根据要求添加:

我使用以下查询来确定符合重建条件的索引。最后的标准可以更改。对于 OLTP 环境中的生产使用,它们足以充分减少不必要的消息,但仍能检测到真正的坏情况。以特权用户身份运行或授予对相关数据字典视图的读取权限。应在 Oracle 8、8i、9 和 10 上运行。最近未在 11 或 12 上使用。

我知道直接查询数据字典并不总是可取的,例如在使用数据模型的多个版本时,但这样做的效果要好得多。受益于重建的索引可以通过存储受益(在黑暗时代,我们在 VMS 服务器上只有几 MB,每个 KB 都很有价值),有时还可以提高性能。

ttitle "Warning: Fragmented Indexes (&dbname)" -
      skip 2 "Corrective action:" -
      skip 1 "Rebuild index (rebuildIndexes.sql)." -
      skip 2

column owner       format a30         heading "Owner"
column ind_name    format a30         heading "Index"
column num_rows    format 999,999,990 heading "#Rows"
column cur_size_kb format 9,999,990   heading "Current Size (Kb)"
column est_size_kb format 9,999,990   heading "Est. Size (Kb)"
column ratio       format 9,990       heading "Ratio cur/est"

select /*+ rule */ usr.name owner
,      objind.name ind_name
,      round
       (
         ( sum
           ( (tab.rowcnt - head.null_cnt) * col.length * 0.5
             /* 50% average fill */
           )
           * 1.5 /* 75% usage after some delet/update */
           + 5 * ts.blocksize
         ) /1024
       ) est_size_kb
,      round(ts.blocksize * seg.blocks / 1024) cur_size_kb
,      round
       ( ts.blocksize * seg.blocks
         / ( sum((tab.rowcnt - head.null_cnt) * col.length * 0.5) * 1.5
             + 5 * ts.blocksize
           )
       ) ratio
,      tab.rowcnt num_rows
from   sys.ind$ ind
,      sys.hist_head$ head
,      sys.col$ col
,      sys.icol$ icol
,      sys.obj$ objind
,      sys.obj$ objtab
,      sys.tab$ tab
,      sys.ts$  ts
,      sys.seg$ seg
,      sys.user$ usr
where  1=1
and    ts.ts# = seg.ts#
and    seg.file# = ind.file#
and    seg.block# = ind.block#
and    ind.obj# = objind.obj#
and    head.col# (+) = col.col#
and    head.obj# (+) = col.obj#
and    icol.obj# = ind.obj#
and    col.col# = icol.col#
and    col.obj# = objtab.obj#
       /* To save at least 25 Mb, the index must be over 25 Mb. */
and    ts.blocksize * seg.blocks > 25 * 1024
and    ind.bo# = objtab.obj#
and    objind.obj# = ind.obj#
and    tab.obj# = objtab.obj#
and    objtab.owner# = usr.user#
and    usr.name not in ('SYS','SYSTEM')
group
by     objind.name
,      ts.blocksize
,      seg.blocks
,      tab.rowcnt
,      usr.name
having ts.blocksize * seg.blocks / ( sum((tab.rowcnt - head.null_cnt) * col.length * 0.5) * 1.5 + 5 * ts.blocksize )
       >= 2
and    ( ts.blocksize * seg.blocks / 1024 )
       -
       ( sum((tab.rowcnt - head.null_cnt) * col.length * 0.5) * 1.5
         + 5 * ts.blocksize
       ) / 1024
       > 25 * 1024
order by 5 desc

【讨论】:

表上还有其他索引吗?对示例运行进行跟踪,看看发生了什么。 在这种情况下,您所说的“碎片化”是什么意思,重建有什么帮助? 重建将所有行点压缩成最小的可能集合。减少 I/O,有时是极端的。还提高了内存的重用性。我记得(在 www 流行之前)来回行驶 1.000 公里只是为了在客户站点重建索引:-)。背景:google,或jonathanlewis.wordpress.com/2010/07/22/fragmentation-4或docs.oracle.com/cd/B19306_01/server.102/b14231/indexes.htm

以上是关于UUID 作为主键(慢插入)的主要内容,如果未能解决你的问题,请参考以下文章

mysql创建数据库时怎么将主键设置为UUID,建表语句怎么写

聊聊用 UUID/GUID 作为主键那些坑

如何将 bigIncrements('id') 转换为 uuid('id') 作为主键?

使用带有 Android 的 Room 使用 UUID 作为主键

PythonDjango Model 怎么使用 UUID 作为主键?

为什么分布式数据库中不使用uuid作为主键?