{"id":8109,"date":"2026-01-26T00:51:46","date_gmt":"2026-01-25T23:51:46","guid":{"rendered":"https:\/\/launix.de\/launix\/?p=8109"},"modified":"2026-01-26T00:51:47","modified_gmt":"2026-01-25T23:51:47","slug":"memcp-january-2025-parallel-sharding-subselects-and-mysql-compatibility","status":"publish","type":"post","link":"https:\/\/launix.de\/launix\/en\/memcp-january-2025-parallel-sharding-subselects-and-mysql-compatibility\/","title":{"rendered":"MemCP January 2025: Parallel Sharding, Subselects, and MySQL Compatibility"},"content":{"rendered":"<p><em>Supercharging query performance with automatic parallel execution<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>After an intense month of development, MemCP has gained some powerful new capabilities that dramatically improve both performance and SQL compatibility. Here&#8217;s what&#8217;s new.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Parallel Sharding: From 1 Core to 19 Cores<\/h2>\n\n\n\n<p>The biggest change this month is <strong>automatic parallel sharding<\/strong>. Previously, bulk-inserted data could end up in a single shard, meaning queries ran on just one CPU core. Now MemCP automatically distributes data across multiple shards for parallel query execution.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Numbers<\/h3>\n\n\n\n<p>On a 24-core machine with 100 million rows:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Query<\/th><th>Time<\/th><th>CPU Utilization<\/th><\/tr><\/thead><tbody><tr><td>COUNT(*)<\/td><td>4.3s<\/td><td>1522%<\/td><\/tr><tr><td>SUM(col)<\/td><td>4.4s<\/td><td>1572%<\/td><\/tr><tr><td>ORDER BY LIMIT<\/td><td>0.9s<\/td><td>1858%<\/td><\/tr><tr><td>Full scan with filter<\/td><td>5.6s<\/td><td>1905%<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>That&#8217;s <strong>0.04 microseconds per row<\/strong> for aggregations\u2014approaching memory bandwidth limits.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How It Works<\/h3>\n\n\n\n<p>When you insert millions of rows at once, MemCP now:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Splits the insert into 60,000-row chunks<\/strong><\/li>\n\n\n\n<li><strong>Creates a new shard for each chunk<\/strong><\/li>\n\n\n\n<li><strong>Rebuilds full shards in parallel<\/strong> using background goroutines<\/li>\n<\/ol>\n\n\n\n<p>During query execution, all shards are scanned concurrently. With 100 million rows split across ~48 shards, you get true parallel execution utilizing all available cores.<\/p>\n\n\n\n<p>Even better: if you haven&#8217;t run any filtered queries yet (which normally guide partitioning decisions), MemCP now falls back to <strong>automatic round-robin sharding<\/strong>, ensuring parallel execution from the start.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Subselects: IN, EXISTS, and Scalar Queries<\/h2>\n\n\n\n<p>MemCP now supports subselects in expressions\u2014a frequently requested feature.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">IN and EXISTS<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>-- Find customers with orders\nSELECT * FROM customers\nWHERE id IN (SELECT customer_id FROM orders)\n\n-- Find products in stock\nSELECT * FROM products\nWHERE EXISTS (SELECT 1 FROM inventory WHERE product_id = products.id)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Scalar Subselects<\/h3>\n\n\n\n<p>Use subqueries directly in your SELECT list:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT\n    name,\n    (SELECT COUNT(*) FROM orders WHERE customer_id = customers.id) AS order_count,\n    (SELECT MAX(amount) FROM orders WHERE customer_id = customers.id) AS max_order\nFROM customers<\/code><\/pre>\n\n\n\n<p>Scalar subselects return NULL if no rows match, or raise an error if multiple rows would be returned.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Derived Tables<\/h3>\n\n\n\n<p>Subselects in the FROM clause (derived tables) now work correctly with JOINs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT t.* FROM (\n    SELECT customer_id, SUM(amount) AS total\n    FROM orders\n    GROUP BY customer_id\n) AS t\nLEFT JOIN customers ON customers.id = t.customer_id\nWHERE t.total &gt; 1000<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL and PostgreSQL Compatibility<\/h2>\n\n\n\n<p>We&#8217;ve added several compatibility features that make migrating from MySQL or PostgreSQL easier.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">INSERT IGNORE (MySQL)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT IGNORE INTO users (id, email) VALUES (1, 'test@example.com')\n-- Silently ignores if id=1 already exists<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">ON DUPLICATE KEY UPDATE (MySQL)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT INTO counters (id, hits) VALUES (1, 1)\nON DUPLICATE KEY UPDATE hits = hits + 1<\/code><\/pre>\n\n\n\n<p>The <code>VALUES(column)<\/code> function lets you reference the would-be-inserted value:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT INTO users (id, name, updated_at) VALUES (1, 'John', NOW())\nON DUPLICATE KEY UPDATE\n    name = VALUES(name),\n    updated_at = VALUES(updated_at)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">ON CONFLICT (PostgreSQL)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT INTO users (id, email) VALUES (1, 'test@example.com')\nON CONFLICT DO NOTHING\n\nINSERT INTO users (id, email, name) VALUES (1, 'test@example.com', 'John')\nON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">ILIKE (PostgreSQL)<\/h3>\n\n\n\n<p>Case-insensitive pattern matching:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT * FROM users WHERE name ILIKE '%john%'\n-- Matches 'John', 'JOHN', 'johnny', etc.<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Collation Support<\/h3>\n\n\n\n<p>Control case sensitivity at the query or column level:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-- Case-sensitive comparison\nSELECT * FROM users WHERE name COLLATE 'bin' = 'John'\n\n-- Column-level collation\nCREATE TABLE t (name VARCHAR(100) COLLATE 'general_ci')<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Performance Testing Framework<\/h2>\n\n\n\n<p>To catch performance regressions early, we&#8217;ve built an <strong>auto-calibrating benchmark framework<\/strong> into the test suite.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Run performance tests\nPERF_TEST=1 make test\n\n# Calibrate for your machine\nPERF_TEST=1 PERF_CALIBRATE=1 make test<\/code><\/pre>\n\n\n\n<p>The framework:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Auto-scales row counts<\/strong> to achieve 10-20 second query times<\/li>\n\n\n\n<li><strong>Stores machine-specific baselines<\/strong> in <code>.perf_baseline.json<\/code><\/li>\n\n\n\n<li><strong>Measures CPU utilization<\/strong> alongside query time<\/li>\n\n\n\n<li><strong>Runs warmup queries<\/strong> before the measured execution<\/li>\n<\/ul>\n\n\n\n<p>Output shows everything you need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u2705 Perf: COUNT (4.3s \/ 13s, 100M rows, 0.04\u00b5s\/row, 25GB heap, 1522%\/2400% CPU)<\/code><\/pre>\n\n\n\n<p>For bisecting regressions, freeze the row counts with <code>PERF_NORECALIBRATE=1<\/code> and let <code>git bisect<\/code> find the problematic commit.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Internal Improvements<\/h2>\n\n\n\n<p>Under the hood, we&#8217;ve made significant changes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Scmer type system rework<\/strong>: The internal value representation was refactored for better performance and GC behavior<\/li>\n\n\n\n<li><strong>FastDict optimization<\/strong>: Hash-based lookups are now transparent to the type system<\/li>\n\n\n\n<li><strong>RDF parser fixes<\/strong>: TTL files with multiple facts and prefix declarations now parse correctly<\/li>\n\n\n\n<li><strong>NonLockingReadMap fix<\/strong>: A subtle race condition in concurrent map access was fixed<\/li>\n\n\n\n<li><strong>LEFT JOIN semantics<\/strong>: Derived tables in LEFT JOINs now correctly preserve WHERE clause behavior<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">What&#8217;s Next<\/h2>\n\n\n\n<p>With parallel execution working well, our focus shifts to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Distributed queries<\/strong> across multiple MemCP nodes<\/li>\n\n\n\n<li><strong>Cost-based query optimization<\/strong> using runtime statistics<\/li>\n\n\n\n<li><strong>Incremental view maintenance<\/strong> for real-time dashboards<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Try It Out<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>git clone https:\/\/github.com\/launix-de\/memcp\ncd memcp\ngo build\n.\/memcp lib\/main.scm<\/code><\/pre>\n\n\n\n<p>Then connect with any MySQL client on port 4321, or use the REST API directly.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><em>MemCP is an open-source, high-performance in-memory database. Star us on <a href=\"https:\/\/github.com\/launix-de\/memcp\">GitHub<\/a> or read the <a href=\"https:\/\/memcp.org\/wiki\/\">documentation<\/a>.<\/em><\/p>","protected":false},"excerpt":{"rendered":"<p>Supercharging query performance with automatic parallel execution After an intense month of development, MemCP has gained some powerful new capabilities that dramatically improve both performance and SQL compatibility. Here&#8217;s what&#8217;s new. Parallel Sharding: From 1 Core to 19 Cores The biggest change this month is automatic parallel sharding. Previously, bulk-inserted data could end up in&#8230;<\/p>","protected":false},"author":2,"featured_media":6010,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","_uag_custom_page_level_css":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-8109","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-allgemein","single-item"],"featured_image_urls_v2":{"full":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false],"thumbnail":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo-150x150.png",150,150,true],"medium":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false],"medium_large":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false],"large":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false],"1536x1536":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false],"2048x2048":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false],"trp-custom-language-flag":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo-14x12.png",14,12,true],"xs-thumb":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo-64x64.png",64,64,true],"appku-shop-single":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false]},"post_excerpt_stackable_v2":"<p>Supercharging query performance with automatic parallel execution After an intense month of development, MemCP has gained some powerful new capabilities that dramatically improve both performance and SQL compatibility. Here&#8217;s what&#8217;s new. Parallel Sharding: From 1 Core to 19 Cores The biggest change this month is automatic parallel sharding. Previously, bulk-inserted data could end up in a single shard, meaning queries ran on just one CPU core. Now MemCP automatically distributes data across multiple shards for parallel query execution. The Numbers On a 24-core machine with 100 million rows: QueryTimeCPU UtilizationCOUNT(*)4.3s1522%SUM(col)4.4s1572%ORDER BY LIMIT0.9s1858%Full scan with filter5.6s1905% That&#8217;s 0.04 microseconds per row&hellip;<\/p>\n","category_list_v2":"<a href=\"https:\/\/launix.de\/launix\/en\/category\/allgemein\/\" rel=\"category tag\">Allgemein<\/a>","author_info_v2":{"name":"Carl-Philip H\u00e4nsch","url":"https:\/\/launix.de\/launix\/en\/author\/carli\/"},"comments_num_v2":"0 comments","uagb_featured_image_src":{"full":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false],"thumbnail":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo-150x150.png",150,150,true],"medium":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false],"medium_large":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false],"large":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false],"1536x1536":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false],"2048x2048":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false],"trp-custom-language-flag":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo-14x12.png",14,12,true],"xs-thumb":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo-64x64.png",64,64,true],"appku-shop-single":["https:\/\/launix.de\/launix\/wp-content\/uploads\/2024\/06\/memcp-logo.png",256,216,false]},"uagb_author_info":{"display_name":"Carl-Philip H\u00e4nsch","author_link":"https:\/\/launix.de\/launix\/en\/author\/carli\/"},"uagb_comment_info":0,"uagb_excerpt":"Supercharging query performance with automatic parallel execution After an intense month of development, MemCP has gained some powerful new capabilities that dramatically improve both performance and SQL compatibility. Here&#8217;s what&#8217;s new. Parallel Sharding: From 1 Core to 19 Cores The biggest change this month is automatic parallel sharding. Previously, bulk-inserted data could end up in...","_links":{"self":[{"href":"https:\/\/launix.de\/launix\/en\/wp-json\/wp\/v2\/posts\/8109","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/launix.de\/launix\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/launix.de\/launix\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/launix.de\/launix\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/launix.de\/launix\/en\/wp-json\/wp\/v2\/comments?post=8109"}],"version-history":[{"count":1,"href":"https:\/\/launix.de\/launix\/en\/wp-json\/wp\/v2\/posts\/8109\/revisions"}],"predecessor-version":[{"id":8110,"href":"https:\/\/launix.de\/launix\/en\/wp-json\/wp\/v2\/posts\/8109\/revisions\/8110"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/launix.de\/launix\/en\/wp-json\/wp\/v2\/media\/6010"}],"wp:attachment":[{"href":"https:\/\/launix.de\/launix\/en\/wp-json\/wp\/v2\/media?parent=8109"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/launix.de\/launix\/en\/wp-json\/wp\/v2\/categories?post=8109"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/launix.de\/launix\/en\/wp-json\/wp\/v2\/tags?post=8109"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}