暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

从存储角度看Zheap

1515

作者简介

Hans-Jürgen Schönig从90年代开始接触PostgreSQL。他是CYBERTEC的CEO以及技术主管,CYBERTEC是该领域的市场领导者之一,自2000年以来已为全球无数客户提供服务。

译者简介

陈雁飞,开源PostgreSQL 爱好者,一直从事PostgreSQL数据库运维工作。

校对者简介    

崔鹏,任职于海能达通信股份有限公司,数据库开发高级工程师,致力于postgresql数据库在专网通信领域、公共安全领域的应用与推广。

为了更加深入地介绍zheap和PostgreSQL在存储方面的技术,先提供一些关于空间消耗的经验信息。正如我上一篇博客中介绍的那样,zheap在空间使用上更加高效。主要原因有:
l 更小的元组头部信息
l 高效地字节对齐能力
现在的问题是:尽管前面理论语句描述是正确的,但是仍然有人希望了解这在实际中意味着什么。这篇博客将对这个问题简单介绍,并给出一些个人的经验性见解,这样在zheap正式发布前每个人都有自己的期望(不会在2020年10月发布)。

分别创建基于PostgreSQL heap和zheap的样本数据

为了说明存储消耗之间的差异,这里先创建一些样本数据。为了公平起见,首先使用临时表将数据保存在内存中。这样不会产生任何其它方面影响:
    test=# SET temp_buffers TO '1 GB';
    SET
    test=# CREATE TEMP TABLE raw AS
    SELECT  id,
    hashtext(id::text) as name,
    random() * 10000 AS n, true AS b
    FROM generate_series(1, 10000000) AS id;
    SELECT 10000000
    一千万条记录大致产生500M数据
      test=# \d+
                              List of relations
        Schema   | Name | Type  | Owner | Persistence |  Size  | Description
      -----------+------+-------+-------+-------------+--------+-------------
       pg_temp_5 | raw  | table | hs    | temporary   | 498 MB |
      (1 row)
      对于这篇博客的目的来说,一张标准的临时表已经足够了。

      填充Zheap表

      个人非常喜欢PostgreSQL中的CREATE TABLE … LIKE … 特性。它允许我们非常迅速地创建相同的表。利用这个特性可以轻松地克隆一张表包含有很多列同时不需要将这些列全部列举出来,比如手工创建索引等。
        test=# \timing
        Timing is on.
        test=# CREATE TABLE h1 (LIKE raw) USING heap;
        CREATE TABLE
        Time: 7.836 ms
        test=# INSERT INTO h1 SELECT * FROM raw;
        INSERT 0 10000000
        Time: 7495.798 ms (00:07.496)
        下面使用zheap表完成同样的操作。为了使用zheap表,需要使用using zheap语法。
          test=# CREATE TABLE z1 (LIKE raw) USING zheap;
          CREATE TABLE
          Time: 8.045 ms
          test=# INSERT INTO z1 SELECT * FROM raw;
          INSERT 0 10000000
          Time: 27947.516 ms (00:27.948)
          可以看到,创建表需要更长的时间,但是表大小存在巨大的差异。
            test=# \d+
                                    List of relations
              Schema   | Name | Type  | Owner | Persistence |  Size  | Description
            -----------+------+-------+-------+-------------+--------+-------------
             pg_temp_5 | raw  | table | hs    | temporary   | 498 MB |
             public    | h1   | table | hs    | permanent   | 498 MB |
             public    | z1   | table | hs    | permanent   | 251 MB |
            (3 rows)
            Zheap表比PostgreSQL普通存储格式下表大小小将近50%。自然会提出一个问题:为什么会出现这样的情况呢?这里主要有两点原因:
            l 元组头更小
            l 高效的对齐和填充
            首先考虑元组头部信息:新的元组头部只有5个字节大小,几乎每行少了20个字节。仅这一点可以节省大约200M的存储空间。由于将可见性信息从行级移动到页面级别(“事务槽”)。如果列越多,差异的百分比越小。反之,如果表真的很窄(列很少),heap和zheap之间差异就会更加明显。
            注意:对于包含少数列的表,减少存储空间的消耗是一个问题。但是如果表包含X00列,那么这个问题不会存在。

            更新操作以及zheap空间消耗情况

            一般来说,在讨论zheap的时候,更新操作一直是一件重要的事情。下面来看下修改表会发生什么:
              test=# BEGIN;
              BEGIN
              test=*# SELECT pg_size_pretty(pg_relation_size('z1'));
               pg_size_pretty
              ----------------
               251 MB
              (1 row)
               
              test=*# UPDATE z1 SET id = id + 1;
              UPDATE 10000000
              test=*# SELECT pg_size_pretty(pg_relation_size('z1'));
               pg_size_pretty
              ----------------
               251 MB
              (1 row)
              在上面例子中,表中每一行大小是固定的。我们仅仅是简单的修改数据的ID信息。可以看到表的大小没有发生变化。如果对heap表执行这样的操作,那么数据文件大小将翻倍。
              在事务中更新的时候,旧数据仍然需要保存。因此,将这些数据保存在“其它地方”,称之为”undo”:
              [hs@hs-MS-7817 undo]$ pwd
              /home/hs/db13/base/undo
              [hs@hs-MS-7817 undo]$ ls -l | tail -n 10
              -rw-------. 1 hs hs 1048576 Oct  8 12:08 000001.003EC00000
              -rw-------. 1 hs hs 1048576 Oct  8 12:08 000001.003ED00000
              -rw-------. 1 hs hs 1048576 Oct  8 12:08 000001.003EE00000
              -rw-------. 1 hs hs 1048576 Oct  8 12:08 000001.003EF00000
              -rw-------. 1 hs hs 1048576 Oct  8 12:08 000001.003F000000
              -rw-------. 1 hs hs 1048576 Oct  8 12:08 000001.003F100000
              -rw-------. 1 hs hs 1048576 Oct  8 12:08 000001.003F200000
              -rw-------. 1 hs hs 1048576 Oct  8 12:08 000001.003F300000
              -rw-------. 1 hs hs 1048576 Oct  8 12:08 000001.003F400000
              -rw-------. 1 hs hs 1048576 Oct  8 12:08 000001.003F500000
              Undo区域包含一系列文件(每个文件大小1M)来存储undo数据,这些数据对于处理回滚操作非常重要(放置旧数据到表中)。
              换句话说,处理事务并不是免费的,所需的空间只是以不同的方式处理罢了。

              一句话关于回滚

              如果对普通的堆表进行回滚操作基本是没有代价的,这是因为事务可以简单地将产生的垃圾数据丢弃掉。但是如果使用zheap存储数据,情况不是这样。如下所示:
                test=# BEGIN;
                BEGIN
                Time: 0.309 ms
                test=*# UPDATE h1 SET id = id - 1 WHERE id < 100000;
                UPDATE 99999
                Time: 741.518 ms
                test=*# ROLLBACK;
                ROLLBACK
                Time: 0.181 ms
                可以看到回滚非常快,因为什么都没有做。在zheap里面情况却是截然不同
                  test=# BEGIN;
                  BEGIN
                  Time: 0.151 ms
                  test=*# UPDATE z1 SET id = id - 1 WHERE id < 100000;
                  UPDATE 99998
                  Time: 1066.378 ms (00:01.066)
                  test=*# ROLLBACK;
                  ROLLBACK
                  Time: 41.539 ms
                  41ms虽然不多,但仍然远远超过一毫秒的几分之一。当然,虽然回滚慢一些,但是zheap主要解决的是表膨胀问题。从长远看,避免表膨胀是它的主要优势。因此,应该以不同的眼光看待这一性能数据。还应该记住,在大多数情况下,COMMIT比ROLLBACK更有可能。因此,最终ROLLBACK不会成为瓶颈点。

                  Zheap:全力以赴

                  如果你想尝试zheap,建议关注我们的github仓库。这里有所有的源代码。现在没有准备二进制程序包。我们将很快发布Docker容器,以便用户可以更加轻松地尝试这项新技术。

                  结束语

                  我想再次说明的是,zheap目前仍在开发中,没有商用发布。但是,这是一项非常不可思议的技术,再次感谢对Heroic Labs的支持。同时,也要感谢EDB多年来在zheap方面所做的工作。
                   
                  请点击文章底部“阅读原文”查看原文内容




                  PostgreSQL中文社区欢迎广大技术人员投稿
                  投稿邮箱:press@postgres.cn

                  文章转载自PostgreSQL中文社区,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                  评论