{"id":37434,"date":"2025-02-25T07:42:33","date_gmt":"2025-02-25T06:42:33","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/?p=37434"},"modified":"2025-02-25T07:42:36","modified_gmt":"2025-02-25T06:42:36","slug":"pg_mooncake-another-columnar-storage-for-postgresql","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/","title":{"rendered":"pg_mooncake: (another) Columnar storage for PostgreSQL"},"content":{"rendered":"\n<p>A very, very long time ago I&#8217;ve written a blog about <a href=\"https:\/\/github.com\/citusdata\/cstore_fdw\/blob\/master\/README.md\">cstore_fdw<\/a>, which brings columnar storage to PostgreSQL. This is now part of <a href=\"https:\/\/www.citusdata.com\/\">Citus<\/a> and does not anymore come as a separate extension. While this still can be used there is another option if you&#8217;re looking for columnar storage in PostgreSQL, and this is <a href=\"https:\/\/pgmooncake.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">pg_mooncake<\/a>. The goal of this extension is to optimize PostgreSQL for analytics and stores the tables in <a href=\"https:\/\/github.com\/apache\/iceberg\">Iceberg<\/a> or <a href=\"https:\/\/github.com\/delta-io\/delta\">Delta Lake<\/a> format, either on local disk or on cloud storage such as s3.<\/p>\n\n\n\n<p>Getting the extension onto the system is pretty simple and straight forward (as you can see below it uses <a href=\"https:\/\/duckdb.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">DuckDB<\/a> in the background), but it will take some time to compile and you need to have <a href=\"https:\/\/github.com\/rust-lang\/cargo\" target=\"_blank\" rel=\"noreferrer noopener\">Cargo<\/a> installed for this to succeed:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; highlight: [1,3,9,10,16,25]; title: ; notranslate\" title=\"\">\npostgres@pgbox:\/home\/postgres\/ &#x5B;172] which pg_config\n\/u01\/app\/postgres\/product\/17\/db_2\/bin\/pg_config\npostgres@pgbox:\/home\/postgres\/ &#x5B;172] git clone https:\/\/github.com\/Mooncake-Labs\/pg_mooncake.git\nCloning into &#039;pg_mooncake&#039;...\nremote: Enumerating objects: 1085, done.\nremote: Counting objects: 100% (533\/533), done.\nremote: Compressing objects: 100% (250\/250), done.\nremote: Total 1085 (delta 406), reused 284 (delta 283), pack-reused 552 (from 2)\nReceiving objects: 100% (1085\/1085), 728.27 KiB | 3.98 MiB\/s, done.\nResolving deltas: 100% (631\/631), done.\npostgres@pgbox:\/home\/postgres\/ &#x5B;172] cd pg_mooncake\/\npostgres@pgbox:\/home\/postgres\/pg_mooncake\/ &#x5B;172] git submodule update --init --recursive\nSubmodule &#039;third_party\/duckdb&#039; (https:\/\/github.com\/duckdb\/duckdb.git) registered for path &#039;third_party\/duckdb&#039;\nCloning into &#039;\/home\/postgres\/pg_mooncake\/third_party\/duckdb&#039;...\nSubmodule path &#039;third_party\/duckdb&#039;: checked out &#039;19864453f7d0ed095256d848b46e7b8630989bac&#039;\npostgres@pgbox:\/home\/postgres\/pg_mooncake\/ &#x5B;172] make release -j2\n...\n&#x5B; 23%] Building CXX object src\/common\/types\/column\/CMakeFiles\/duckdb_common_types_column.dir\/ub_duckdb_common_types_column.cpp.o\n&#x5B; 23%] Built target duckdb_common_types_column\n&#x5B; 23%] Building CXX object src\/common\/types\/row\/CMakeFiles\/duckdb_common_types_row.dir\/ub_duckdb_common_types_row.cpp.o\n&#x5B; 23%] Built target duckdb_common_types_row\n&#x5B; 23%] Building CXX object src\/common\/value_operations\/CMakeFiles\/duckdb_value_operations.dir\/ub_duckdb_value_operations.cpp.o\n&#x5B; 23%] Built target duckdb_common_types\n...\npostgres@pgbox:\/home\/postgres\/pg_mooncake\/ &#x5B;172] make install\n\/usr\/bin\/mkdir -p &#039;\/u01\/app\/postgres\/product\/17\/db_2\/lib&#039;\n\/usr\/bin\/mkdir -p &#039;\/u01\/app\/postgres\/product\/17\/db_2\/share\/extension&#039;\n\/usr\/bin\/mkdir -p &#039;\/u01\/app\/postgres\/product\/17\/db_2\/share\/extension&#039;\n\/usr\/bin\/mkdir -p &#039;\/u01\/app\/postgres\/product\/17\/db_2\/lib&#039;\n\/usr\/bin\/install -c -m 755  pg_mooncake.so &#039;\/u01\/app\/postgres\/product\/17\/db_2\/lib\/pg_mooncake.so&#039;\n\/usr\/bin\/install -c -m 644 .\/\/..\/..\/pg_mooncake.control &#039;\/u01\/app\/postgres\/product\/17\/db_2\/share\/extension\/&#039;\n\/usr\/bin\/install -c -m 644 .\/\/..\/..\/sql\/pg_mooncake--0.1.0.sql .\/\/..\/..\/sql\/pg_mooncake--0.1.0--0.1.1.sql .\/\/..\/..\/sql\/pg_mooncake--0.1.1--0.1.2.sql  &#039;\/u01\/app\/postgres\/product\/17\/db_2\/share\/extension\/&#039;\n\/usr\/bin\/install -c -m 755  libduckdb.so &#039;\/u01\/app\/postgres\/product\/17\/db_2\/lib\/&#039;\n<\/pre><\/div>\n\n\n<p>On this is compiled and installed simply add the extension to a database:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [1,3]; title: ; notranslate\" title=\"\">\npostgres=$ create extension pg_mooncake;\nCREATE EXTENSION\npostgres=$ \\dx\n                                            List of installed extensions\n        Name        | Version |   Schema   |                              Description                               \n--------------------+---------+------------+------------------------------------------------------------------------\n pg_mooncake        | 0.1.2   | public     | Columnstore Table in Postgres\n pg_stat_statements | 1.11    | public     | track planning and execution statistics of all SQL statements executed\n plpgsql            | 1.0     | pg_catalog | PL\/pgSQL procedural language\n(3 rows)\n<\/pre><\/div>\n\n\n<p>To compare this table layout against the standard PostgreSQL layout, let&#8217;s create two tables, one of them using the column store format:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [1,3,5,13]; title: ; notranslate\" title=\"\">\npostgres=# create table t1 ( a int, b text );\nCREATE TABLE\npostgres=# create table t2 ( a int, b text ) using columnstore;\nCREATE TABLE\npostgres=# \\d+ t1\n                                            Table &quot;public.t1&quot;\n Column |  Type   | Collation | Nullable | Default | Storage  | Compression | Stats target | Description \n--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------\n a      | integer |           |          |         | plain    |             |              | \n b      | text    |           |          |         | extended |             |              | \nAccess method: heap\n\npostgres=# \\d+ t2\n                                            Table &quot;public.t2&quot;\n Column |  Type   | Collation | Nullable | Default | Storage  | Compression | Stats target | Description \n--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------\n a      | integer |           |          |         | plain    |             |              | \n b      | text    |           |          |         | extended |             |              | \nAccess method: columnstore\n<\/pre><\/div>\n\n\n<p>Adding a millions rows to both of the tables:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [1,3]; title: ; notranslate\" title=\"\">\npostgres=# insert into t1 select i, md5(i::text) from generate_series(1,1000000) i;\nINSERT 0 1000000\npostgres=# insert into t2 select i, md5(i::text) from generate_series(1,1000000) i;\nINSERT 0 1000000\n<\/pre><\/div>\n\n\n<p>Looking at the disk we can see the first surprise:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [1,7,13,19]; title: ; notranslate\" title=\"\">\npostgres=# select pg_relation_filepath(&#039;t1&#039;);\n pg_relation_filepath \n----------------------\n base\/5\/24715\n(1 row)\n\npostgres=# select pg_relation_filepath(&#039;t2&#039;);\n pg_relation_filepath \n----------------------\n base\/5\/24718\n(1 row)\n\npostgres=# select pg_size_pretty ( pg_relation_size ( &#039;t1&#039; ));\n pg_size_pretty \n----------------\n 65 MB\n(1 row)\n\npostgres=# select pg_size_pretty ( pg_relation_size ( &#039;t2&#039; ));\n pg_size_pretty \n----------------\n 0 bytes\n(1 row)\n<\/pre><\/div>\n\n\n<p>The table in columnar format reports a size of zero bytes, and indeed there is not even a file on disk which corresponds to 24718:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [1,3]; title: ; notranslate\" title=\"\">\npostgres=# \\! ls -la $PGDATA\/base\/5\/24715\n-rw-------. 1 postgres postgres 68272128 Feb 24 14:19 \/u02\/pgdata\/17\/base\/5\/24715\npostgres=# \\! ls -la $PGDATA\/base\/5\/24718\nls: cannot access &#039;\/u02\/pgdata\/17\/base\/5\/24718&#039;: No such file or directory\n<\/pre><\/div>\n\n\n<p>Instead the table is stored here as <a href=\"https:\/\/parquet.apache.org\/#td-block-1\">Parquet<\/a> files:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n postgres@pgbox:\/u02\/pgdata\/17\/ &#x5B;172] ls -la $PGDATA\/mooncake_local_tables\/mooncake_postgres_t2_24708\/\ntotal 107224\ndrwx------. 3 postgres postgres      180 Feb 24 14:31 .\ndrwx------. 3 postgres postgres       40 Feb 24 14:15 ..\n-rw-------. 1 postgres postgres 36596064 Feb 24 14:19 560c6efe-1226-4a76-985f-1301169bcc44.parquet\n-rw-------. 1 postgres postgres 36596064 Feb 24 14:18 ca0550d6-bd84-4bf9-b8cf-6ce85a65346b.parquet\ndrwx------. 2 postgres postgres     4096 Feb 24 14:19 _delta_log\n-rw-------. 1 postgres postgres 36596064 Feb 24 14:19 fba0eff4-3c57-4dbb-bd9b-469f6622ab92.parquet\n<\/pre><\/div>\n\n\n<p><a href=\"https:\/\/www.mooncake.dev\/blog\/how-we-built-pgmooncake\" target=\"_blank\" rel=\"noreferrer noopener\">Here<\/a> is more detailed blog about the design decisions.<\/p>\n\n\n\n<p>The difference is also visible when you look at the explain plans against both tables:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [1,15]; title: ; notranslate\" title=\"\">\npostgres=# explain (analyze) select * from t1 where a = 1;\n                                                   QUERY PLAN                                                    \n-----------------------------------------------------------------------------------------------------------------\n Gather  (cost=1000.00..14542.43 rows=1 width=37) (actual time=1.591..41.014 rows=1 loops=1)\n   Workers Planned: 2\n   Workers Launched: 2\n   -&amp;gt;  Parallel Seq Scan on t1  (cost=0.00..13542.33 rows=1 width=37) (actual time=0.022..11.067 rows=0 loops=3)\n         Filter: (a = 1)\n         Rows Removed by Filter: 333333\n Planning Time: 0.861 ms\n Execution Time: 41.086 ms\n(8 rows)\n\n\npostgres=# explain (analyze) select * from t2 where a = 1;\n                                                  QUERY PLAN                                                  \n--------------------------------------------------------------------------------------------------------------\n Custom Scan (MooncakeDuckDBScan)  (cost=0.00..0.00 rows=0 width=0) (actual time=7.797..7.816 rows=1 loops=1)\n   DuckDB Execution Plan: \n \n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u2502\n \u2502\u2502    Query Profiling Information    \u2502\u2502\n \u2502\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n EXPLAIN ANALYZE SELECT a, b FROM pgmooncake.public.t2 WHERE (a = 1)\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u2502\n \u2502\u2502              Total Time: 0.0043s             \u2502\u2502\n \u2502\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502           QUERY           \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502      EXPLAIN_ANALYZE      \u2502\n \u2502    \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500   \u2502\n \u2502           0 Rows          \u2502\n \u2502          (0.00s)          \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502         TABLE_SCAN        \u2502\n \u2502    \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500   \u2502\n \u2502         Function:         \u2502\n \u2502      COLUMNSTORE_SCAN     \u2502\n \u2502                           \u2502\n \u2502        Projections:       \u2502\n \u2502             a             \u2502\n \u2502             b             \u2502\n \u2502                           \u2502\n \u2502          Filters:         \u2502\n \u2502   a=1 AND a IS NOT NULL   \u2502\n \u2502                           \u2502\n \u2502           1 Rows          \u2502\n \u2502          (0.00s)          \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \n \n Planning Time: 1.693 ms\n Execution Time: 8.584 ms\n(43 rows)\n<\/pre><\/div>\n\n\n<p>As usual with columnar data storage, is is best when you have data which can be compressed well on a columnar basis, e.g.:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [1,3,6]; title: ; notranslate\" title=\"\">\npostgres=# truncate t1,t2;\nTRUNCATE TABLE\npostgres=# insert into t1 select 1, md5(i::text) from generate_series(1,1000000) i;\nINSERT 0 1000000\nTime: 852.812 ms\npostgres=# insert into t2 select 1, md5(i::text) from generate_series(1,1000000) i;\nINSERT 0 1000000\nTime: 243.532 ms\n<\/pre><\/div>\n\n\n<p>The insert into the t2 is consistently faster than the insert into the standard tables (just repeat the inserts multiple times to get an idea). The same happens when you read columns which are compressed well (same here, just repeat the query multiple times to get an idea):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [1,8]; title: ; notranslate\" title=\"\">\npostgres=# select count(a) from t1;\n  count  \n---------\n 2000000\n(1 row)\n\nTime: 60.463 ms\npostgres=# select count(a) from t2;\n  count  \n---------\n 2000000\n(1 row)\n\nTime: 10.272 ms\n<\/pre><\/div>\n\n\n<p>This might be an option if you have use cases for this.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A very, very long time ago I&#8217;ve written a blog about cstore_fdw, which brings columnar storage to PostgreSQL. This is now part of Citus and does not anymore come as a separate extension. While this still can be used there is another option if you&#8217;re looking for columnar storage in PostgreSQL, and this is pg_mooncake. [&hellip;]<\/p>\n","protected":false},"author":29,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[229,198],"tags":[77],"type_dbi":[],"class_list":["post-37434","post","type-post","status-publish","format-standard","hentry","category-database-administration-monitoring","category-database-management","tag-postgresql"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.2 (Yoast SEO v27.2) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>pg_mooncake: (another) Columnar storage for PostgreSQL - dbi Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"pg_mooncake: (another) Columnar storage for PostgreSQL\" \/>\n<meta property=\"og:description\" content=\"A very, very long time ago I&#8217;ve written a blog about cstore_fdw, which brings columnar storage to PostgreSQL. This is now part of Citus and does not anymore come as a separate extension. While this still can be used there is another option if you&#8217;re looking for columnar storage in PostgreSQL, and this is pg_mooncake. [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2025-02-25T06:42:33+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-02-25T06:42:36+00:00\" \/>\n<meta name=\"author\" content=\"Daniel Westermann\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@westermanndanie\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Daniel Westermann\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"2 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/\"},\"author\":{\"name\":\"Daniel Westermann\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d08e9bd996a89bd75c0286cbabf3c66\"},\"headline\":\"pg_mooncake: (another) Columnar storage for PostgreSQL\",\"datePublished\":\"2025-02-25T06:42:33+00:00\",\"dateModified\":\"2025-02-25T06:42:36+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/\"},\"wordCount\":336,\"commentCount\":0,\"keywords\":[\"PostgreSQL\"],\"articleSection\":[\"Database Administration &amp; Monitoring\",\"Database management\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/\",\"name\":\"pg_mooncake: (another) Columnar storage for PostgreSQL - dbi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\"},\"datePublished\":\"2025-02-25T06:42:33+00:00\",\"dateModified\":\"2025-02-25T06:42:36+00:00\",\"author\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d08e9bd996a89bd75c0286cbabf3c66\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\/\/www.dbi-services.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"pg_mooncake: (another) Columnar storage for PostgreSQL\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/\",\"name\":\"dbi Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d08e9bd996a89bd75c0286cbabf3c66\",\"name\":\"Daniel Westermann\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/secure.gravatar.com\/avatar\/31350ceeecb1dd8986339a29bf040d4cd3cd087d410deccd8f55234466d6c317?s=96&d=mm&r=g\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/31350ceeecb1dd8986339a29bf040d4cd3cd087d410deccd8f55234466d6c317?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/31350ceeecb1dd8986339a29bf040d4cd3cd087d410deccd8f55234466d6c317?s=96&d=mm&r=g\",\"caption\":\"Daniel Westermann\"},\"description\":\"Daniel Westermann is Principal Consultant and Technology Leader Open Infrastructure at dbi services. He has more than 15 years of experience in management, engineering and optimization of databases and infrastructures, especially on Oracle and PostgreSQL. Since the beginning of his career, he has specialized in Oracle Technologies and is Oracle Certified Professional 12c and Oracle Certified Expert RAC\/GridInfra. Over time, Daniel has become increasingly interested in open source technologies, becoming \u201cTechnology Leader Open Infrastructure\u201d and PostgreSQL expert. \u00a0Based on community or EnterpriseDB tools, he develops and installs complex high available solutions with PostgreSQL. He is also a certified PostgreSQL Plus 9.0 Professional and a Postgres Advanced Server 9.4 Professional. He is a regular speaker at PostgreSQL conferences in Switzerland and Europe. Today Daniel is also supporting our customers on AWS services such as AWS RDS, database migrations into the cloud, EC2 and automated infrastructure management with AWS SSM (System Manager). He is a certified AWS Solutions Architect Professional. Prior to dbi services, Daniel was Management System Engineer at LC SYSTEMS-Engineering AG in Basel. Before that, he worked as Oracle Developper &amp;\u00a0Project Manager at Delta Energy Solutions AG in Basel (today Powel AG). Daniel holds a diploma in Business Informatics (DHBW, Germany). His branch-related experience mainly covers the pharma industry, the financial sector, energy, lottery and telecommunications.\",\"sameAs\":[\"https:\/\/x.com\/westermanndanie\"],\"url\":\"https:\/\/www.dbi-services.com\/blog\/author\/daniel-westermann\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"pg_mooncake: (another) Columnar storage for PostgreSQL - dbi Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/","og_locale":"en_US","og_type":"article","og_title":"pg_mooncake: (another) Columnar storage for PostgreSQL","og_description":"A very, very long time ago I&#8217;ve written a blog about cstore_fdw, which brings columnar storage to PostgreSQL. This is now part of Citus and does not anymore come as a separate extension. While this still can be used there is another option if you&#8217;re looking for columnar storage in PostgreSQL, and this is pg_mooncake. [&hellip;]","og_url":"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/","og_site_name":"dbi Blog","article_published_time":"2025-02-25T06:42:33+00:00","article_modified_time":"2025-02-25T06:42:36+00:00","author":"Daniel Westermann","twitter_card":"summary_large_image","twitter_creator":"@westermanndanie","twitter_misc":{"Written by":"Daniel Westermann","Est. reading time":"2 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/"},"author":{"name":"Daniel Westermann","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d08e9bd996a89bd75c0286cbabf3c66"},"headline":"pg_mooncake: (another) Columnar storage for PostgreSQL","datePublished":"2025-02-25T06:42:33+00:00","dateModified":"2025-02-25T06:42:36+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/"},"wordCount":336,"commentCount":0,"keywords":["PostgreSQL"],"articleSection":["Database Administration &amp; Monitoring","Database management"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/","url":"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/","name":"pg_mooncake: (another) Columnar storage for PostgreSQL - dbi Blog","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"datePublished":"2025-02-25T06:42:33+00:00","dateModified":"2025-02-25T06:42:36+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d08e9bd996a89bd75c0286cbabf3c66"},"breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/pg_mooncake-another-columnar-storage-for-postgresql\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"pg_mooncake: (another) Columnar storage for PostgreSQL"}]},{"@type":"WebSite","@id":"https:\/\/www.dbi-services.com\/blog\/#website","url":"https:\/\/www.dbi-services.com\/blog\/","name":"dbi Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d08e9bd996a89bd75c0286cbabf3c66","name":"Daniel Westermann","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/31350ceeecb1dd8986339a29bf040d4cd3cd087d410deccd8f55234466d6c317?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/31350ceeecb1dd8986339a29bf040d4cd3cd087d410deccd8f55234466d6c317?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/31350ceeecb1dd8986339a29bf040d4cd3cd087d410deccd8f55234466d6c317?s=96&d=mm&r=g","caption":"Daniel Westermann"},"description":"Daniel Westermann is Principal Consultant and Technology Leader Open Infrastructure at dbi services. He has more than 15 years of experience in management, engineering and optimization of databases and infrastructures, especially on Oracle and PostgreSQL. Since the beginning of his career, he has specialized in Oracle Technologies and is Oracle Certified Professional 12c and Oracle Certified Expert RAC\/GridInfra. Over time, Daniel has become increasingly interested in open source technologies, becoming \u201cTechnology Leader Open Infrastructure\u201d and PostgreSQL expert. \u00a0Based on community or EnterpriseDB tools, he develops and installs complex high available solutions with PostgreSQL. He is also a certified PostgreSQL Plus 9.0 Professional and a Postgres Advanced Server 9.4 Professional. He is a regular speaker at PostgreSQL conferences in Switzerland and Europe. Today Daniel is also supporting our customers on AWS services such as AWS RDS, database migrations into the cloud, EC2 and automated infrastructure management with AWS SSM (System Manager). He is a certified AWS Solutions Architect Professional. Prior to dbi services, Daniel was Management System Engineer at LC SYSTEMS-Engineering AG in Basel. Before that, he worked as Oracle Developper &amp;\u00a0Project Manager at Delta Energy Solutions AG in Basel (today Powel AG). Daniel holds a diploma in Business Informatics (DHBW, Germany). His branch-related experience mainly covers the pharma industry, the financial sector, energy, lottery and telecommunications.","sameAs":["https:\/\/x.com\/westermanndanie"],"url":"https:\/\/www.dbi-services.com\/blog\/author\/daniel-westermann\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/37434","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/users\/29"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=37434"}],"version-history":[{"count":18,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/37434\/revisions"}],"predecessor-version":[{"id":37452,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/37434\/revisions\/37452"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=37434"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=37434"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=37434"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=37434"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}