31

我们正在从 MySQL 迁移到 PGSQL,我们有一个 1 亿行的表。

当我试图确定两个系统使用了多少空间时,我发现表的差异要小得多,但发现索引的差异很大。

MySQL 索引占用的大小比表数据本身大,而 postgres 使用的大小要小得多。

  • 在挖掘原因时,我发现 MySQL 使用 B+ 树来存储索引,而 postgres使用B-tree。

  • MySQL 对索引的使用有点不同,它将数据与索引一起存储(由于增加了大小),但 postgres 没有。

现在的问题:

  • 比较数据库中的 B-tree 和 B+ 树,最好使用 B+tree,因为它们更适合范围查询 O(m) + O(logN) - 其中范围和查找中的 m 在 B+tree 中是对数的?

    现在在 B 树中,对于范围查询,查找是对数的,因为它没有数据节点的链表底层结构,所以它会上升到 O(N)。话虽如此,为什么 postgres 使用 B-trees?它对范围查询是否表现良好(确实如此,但它如何在内部处理 B 树)?

  • 上面的问题是从postgres的角度来看的,但是从MySQL的角度来看,为什么它比postgres使用更多的存储空间,在现实中使用B+trees有什么性能优势呢?

我可能错过/误解了很多事情,所以请随时在这里纠正我的理解。

编辑以回答 Rick James 的问题

  • 我正在为 MySQL 使用 InnoDB 引擎
  • 我在填充数据后建立了索引 - 与我在 postgres 中所做的相同
  • 索引不是唯一索引,只是普通索引
  • 没有随机插入,我在 postgres 和 MySQL 中都使用了 csv 加载,然后才创建索引。
  • 索引和数据的 Postgres 块大小都是 8KB,我不确定 MySQL,但我没有更改它,所以它必须是默认值。
  • 我不会称这些行很大,它们有大约 4 个文本字段,长度为 200 个字符,4 个十进制字段和 2 个 bigint 字段 - 19 个数字长。
  • PK 是一个 bigint 列,有 19 个数字,不知道这算不算笨重?应该在什么规模上区分笨重与非笨重?
  • MySQL 表大小为 600 MB,Postgres 大约 310 MB,包括索引 - 如果我的数学是正确的,这相当于大 48%。但是有没有一种方法可以单独测量 MySQL 中的索引大小,不包括表大小?我猜这可以带来更好的数字。
  • 机器信息:我有足够的 RAM - 256GB 来将所有表和索引放在一起,但我认为我们根本不需要遍历这条路线,我没有看到它们之间有任何明显的性能差异。

附加问题

  • 当我们说碎片发生时?有没有办法进行碎片整理,以便我们可以说除此之外,没有什么可做的。顺便说一下,我正在使用 Cent OS。
  • 有没有办法在 MySQL 中测量索引大小,忽略主键,因为它是聚集的,这样我们就可以实际看到什么类型占用了更多的大小(如果有的话)。
4

3 回答 3

10

首先,最重要的是,如果您不使用InnoDB,请关闭此问题,使用 InnoDB 重建,然后查看是否需要重新打开问题。MyISAM不是首选,不应讨论。

你是如何在 MySQL 中建立索引的?有几种方法可以显式或隐式地构建索引;它们会导致更好或更差的包装。

MySQL:数据和索引存储在由16KB块组成的 B+Trees 中。

MySQL: UNIQUE索引(包括PRIMARY KEY必须在插入行时更新。因此,UNIQUE索引必然会有很多块拆分等。

MySQL:它与数据PRIMARY KEY聚集在一起,因此它实际上占用了零空间。如果以 PK 顺序加载数据,则块碎片最小。

UNIQUE辅助密钥可能是动态构建的,这会导致一些碎片。或者它们可以在表格加载后构建;这导致更密集的包装。

辅助键(UNIQUE或不)隐含地包含PRIMARY KEY在其中。如果 PK 为“大”,则辅助键很庞大。你的PK是什么?这是“答案”吗?

理论上,完全随机插入到 BTree 会导致块大约69% full。也许这就是答案。MySQL 是否大了 45% (1/69%)?

对于 100M 行,可能许多操作都是 I/O 绑定的,因为您没有足够的 RAM 来缓存所需的所有数据和/或索引块。如果所有内容都被缓存,那么 B-Tree 与 B+Tree 不会有太大区别。让我们分析当事物没有完全缓存时,范围查询需要发生什么。

对于任一类型的树,操作从树中的向下钻取开始。对于 MySQL,100M 行将具有大约 4 层深的 B+Tree。3 个非叶节点(同样是 16KB 块)将被缓存(如果它们还没有的话)并被重用。即使对于 Postgres,这种缓存也可能发生。(我不知道 Postgres。)然后范围扫描开始。使用 MySQL,它会遍历块的其余部分。(经验法则:一个块中有 100 行。) Postgres 也一样?

在块的末尾,必须发生一些不同的事情。对于 MySQL,有一个指向下一个块的链接。该块(还有 100 多行)是从磁盘中获取的(如果没有缓存的话)。对于 B 树,需要再次遍历非叶节点。2,大概还有3个级别被缓存。我预计需要从磁盘仅获取 1/10K 行的另一个非叶节点。(10K = 100*100) 也就是说,即使在“冷”系统上,Postgres 访问磁盘的频率可能比 MySQL 高 1%。

另一方面,如果行太胖以至于只有 1 或 2 可以放入 16K 块中,我一直使用的“100”更像是“2”,而 1% 可能会变成 50%。也就是说,如果你有大行,这可能是“答案”。是吗?

Postgres 中的块大小是多少? 请注意,上面的许多计算取决于块和数据之间的相对大小。这可能是一个答案吗?

结论: 我给了你4个可能的答案。您是否想扩大问题以确认或反驳每一项都适用?(二级索引存在,PK大,二级索引搭建效率低,行大,块大小,...)

关于 PRIMARY KEY 的附录

对于InnoDB,还有一点要注意……最好PRIMARY KEY在加载数据之前在表的定义中有一个。也最好先将数据按PK顺序排序LOAD DATA。在不指定任何PRIMARY KEYUNIQUE键的情况下,InnoDB 构建一个隐藏的 6 字节 PK;这通常是次优的。

于 2015-10-31T01:18:44.167 回答
3

MySQL 和 PostgreSQL 在这里没有可比性 Innodb 使用索引来存储表数据(二级索引只指向 pkey)。这对于单行 pkey 查找和 B+ 树非常有用,可以在 pkey 字段上进行范围查询,但对其他所有内容都有性能缺陷。

PostgreSQL 使用堆表并将索引分开放置。它支持许多不同的索引算法。根据您的范围查询,btree 索引可能对您没有帮助,您可能需要一个 GiST 索引。类似地,GIN 索引也适用于成员查找(对于数组、fts 等)。

我认为使用 btree 是因为它擅长简单的用例:哪些 roes 包含以下数据?例如,这成为 GIN 的构建块。

但是 PostgreSQL 不能使用 B+ 树是不正确的。GiST 建立在通用格式的 B+ 树索引之上。因此 PostgreSQL 为您提供了使用 B+ 树的选项,它们派上用场。

于 2015-11-01T17:02:01.993 回答
3

在数据库中,您经常查询谁提供了一些数据范围,例如从 100 到 200 的 id。
在这种情况下

  • B-Tree 需要遵循从根到叶子的路径,以获取每个条目的数据指针。
  • B+-Trees 可以“走”过叶子,并且只需要第一次沿着路径到达叶子(即对于 id 100)

这是因为B+-Trees仅将数据(或数据指针)存储在叶子中,并且叶子是链接的,因此您可以执行快速的按顺序遍历。

B+-树 B+-树

另一点是:
在 B+Trees 中,内部节点仅存储指向其他节点的指针,没有任何数据指针,因此您有更多的指针空间,并且您需要更少的 IO 操作,并且您可以在内存页面中存储更多的节点指针.

因此,对于范围查询,B+-Trees 是最佳的数据结构。对于单一选择,B-Trees 可能更好(因为树的深度/大小),因为数据指针也位于树内。

于 2015-10-23T11:18:41.870 回答