{"slug": "vector-search-in-open-source-mysql", "title": "Vector Search in open-source MySQL", "summary": "VillageSQL demoed an early, unmerged build of custom HNSW vector indexes for MySQL at Percona Live in Amsterdam on September 9, 2026, adding vector search that stock MySQL 9.x lacks. In the demo, an HNSW-indexed table of 20,000 3-dimensional vectors averaged 0.19 ms per nearest-neighbor query across 50 queries versus 3.82 ms for a full scan, a 20x difference, while returning nine of the true top ten results. VillageSQL's extension framework supplies a custom SVECTOR column type and a custom index type declared with USING EXTENDED(hnsw), which the server plans as a \"Custom index distance scan on idx_embedding.", "body_md": "# Vector search in MySQL: an early look at HNSW and custom indexes in VillageSQL\n\nMySQL 9.x added the `VECTOR` data type so you can store an embedding. You get a `VECTOR` column, functions to convert to and from text, and `VECTOR_DIM()` to ask how wide a vector is.\n\nWhat you don't get is a way to compare two vectors. The 9.x community server has no distance function and no vector index. MySQL 8.4 has none of this at all. You can't add the missing index yourself either because vanilla MySQL's list of index types is fixed to B-tree, R-tree, hash, and full-text.\n\nThat's where VillageSQL comes in. VillageSQL is the innovation platform for MySQL that adds an extension framework (similar to PostgreSQL's extension framework) to enable permissionless innovation. Instead of waiting for a feature to be implemented in a few years in a future version of MySQL, new functionality can be dynamically added to a version of MySQL you run today.\n\nVillageSQL already supports custom functions and custom data types, and we've been working on custom indexes. With custom indexes, an extension can define a whole index type including how it's stored in InnoDB, how it's built as rows arrive, and how it's searched. The server treats the custom index as a first-class index, the same as the built-in index types. The server even plans queries against the custom index and reports it by name.\n\nVector search is a key feature for AI-era applications, so that's the first use case for custom indexes we are building.\n\nAt Percona Live in Amsterdam on September 9, 2026, we demoed an early build of this vector work (not yet merged). Below is the annotated demo.\n\n## Start with a server\n\nThe demo starts by installing VillageSQL. You copy one line from the website, paste it into a terminal, and you have a prebuilt server.\n\n```\ncurl -fsSL https://install.villagesql.com | bash\n```\n\nThat gets you a stable server. Please note the vector work highlighted below runs on an unmerged, development build.\n\n## Declaring a vector index\n\nWith the server up, the demo installs the `vsql-vector` extension with an ordinary `INSTALL EXTENSION`, and then creates one table:\n\n```\nCREATE TABLE demo_vectors (\n  id INT PRIMARY KEY,\n  embedding SVECTOR(3) NOT NULL,\n  INDEX idx_embedding (embedding hnsw_l2) USING EXTENDED(hnsw)\n) ENGINE=InnoDB;\n```\n\n`USING EXTENDED(hnsw)` names a custom index type and `SVECTOR` is a custom column type. The extension supplies both. The server stores the metadata, plans the query, and hands the work off. Once the table exists, the vectors arrive by plain `INSERT`.\n\n## Does the server use it?\n\nYou can ask the server whether it will really use that index:\n\n```\nEXPLAIN FORMAT=TREE SELECT id FROM demo_vectors\n  ORDER BY l2_distance(embedding, '[1.0,2.0,3.0]') LIMIT 3\\G\n```\n\nThe plan comes back as `Custom index distance scan on idx_embedding`, which is the server saying it would answer from the HNSW graph instead of reading every row. Running that same `SELECT` without `EXPLAIN` in front of it returns the three nearest rows, closest first.\n\n## Fast and correct\n\nThe demo then builds two tables that hold the same 20,000 vectors. One table has an HNSW index on the vector column and the other has no index at all, and the same nearest-neighbor query runs against both.\n\nAcross 50 queries each, the indexed table averages 0.19 ms and the full scan 3.82 ms, which is 20x slower. This is synthetic data at 3 dimensions, so read it as a mechanism check rather than a benchmark.\n\nSpeed alone does not prove an index is beneficial, because an approximate index can be fast by being wrong. The unindexed table holds the same vectors, so it gives the exact answer to compare against. Here the index returns nine of the true top ten, and the tenth row is the price of an approximate search. `ef_search` is a dial that allows you to tune accuracy and performance.\n\n## Where it lands on real embeddings\n\nWe ran ann-benchmarks against fashion-mnist-784 on an 8 vCPU Xeon VM. Below are our initial results. When vector indexes are merged, we’ll publish steps to reproduce these benchmark results.\n\n*fashion-mnist-784* — 60,000 vectors, k=10, 8 vCPU Xeon\n\n| ef_search | queries/sec | recall@10 | \n|---|---|---|\n| 50 | 635 | 99.68% | \n| 100 | 440 | 99.85% | \n| 200 | 290 | 99.92% | \n\nThese numbers are a snapshot of the work which is still in progress. They come from one early build, and we expect them to move. The shape of the curve is the interesting part. Each step up in `ef_search` buys recall and costs throughput. Going from 50 to 100 adds 0.17 points of recall and gives up about 30% of the queries per second. Going to 200 adds another 0.07 points and costs about half. You can choose the point on that curve that your application needs.\n\n## How it fits together\n\nThe server treats the extension's index as a real one. Ask what indexes a table has, and the vector ones come back as `HNSW`, sitting next to an ordinary `BTREE`. `SHOW CREATE TABLE` (from a different example table with two vector columns) returns the full definition:\n\n```\nKEY `idx_title` (`title_vec` `vsql_vector`.` hnsw_cosine`)\n  USING EXTENDED(`vsql_vector`.` hnsw`) WITH (` ef_construction` = 64, `m` = 8),\nKEY `idx_body` (`body_vec` `vsql_vector`.` hnsw_l1`),\nKEY `idx_tag` (`tag`)\n```\n\n`M` and `ef_construction` are the extension's own build knobs. The server stores them without knowing what they mean, and prints them back in DDL you can replay. The vectors themselves sit in InnoDB's own storage, so there's no shadow table and no second write on insert. Columns go up to 3,072 dimensions (for comparison, pgvector supports 2,000 dimensions for an indexed column), and one table can carry several vector indexes, each on its own column with its own distance function.\n\n## What's next\n\nFiltered search, deletes, wider version coverage, and comparison benchmarks are all still ahead. We expect to deliver the custom index framework for both 8.4 and 9.7 codebases.\n\nThis is an initial build of unreleased work, and we wanted to show it while it's still moving. If you want to read the code in the meantime, the extension is at [github.com/villagesql/vsql-vector](https://github.com/villagesql/vsql-vector), and you can get the server from [villagesql.com](https://villagesql.com/). VillageSQL Server supports MySQL 8.4, 9.7, and Percona Server 8.4.\n\nPlease let us know your feedback. You can find us on [Discord](https://discord.gg/KSr6whd3Fr) or on [GitHub Issues](https://github.com/villagesql/villagesql-server/issues).", "url": "https://wpnews.pro/news/vector-search-in-open-source-mysql", "canonical_source": "https://villagesql.com/blog/vector-search-hnsw/", "published_at": "2026-09-16 19:10:30+00:00", "updated_at": "2026-09-16 19:42:21.937317+00:00", "lang": "en", "topics": ["ai-infrastructure", "developer-tools", "ai-tools"], "entities": ["VillageSQL", "MySQL", "Percona Live", "InnoDB", "HNSW", "ann-benchmarks", "fashion-mnist-784", "Xeon"], "alternates": {"html": "https://wpnews.pro/news/vector-search-in-open-source-mysql", "markdown": "https://wpnews.pro/news/vector-search-in-open-source-mysql.md", "text": "https://wpnews.pro/news/vector-search-in-open-source-mysql.txt", "jsonld": "https://wpnews.pro/news/vector-search-in-open-source-mysql.jsonld"}}