Hashfuncs
Fast, deterministic, non-cryptographic hash functions β xxHash, MurmurHash3, RapidHash.
On this page
Technical Overview
Fast, non-cryptographic hashes for keys and buckets
Industry-standard, non-cryptographic hashes inside DuckDB SQL β xxHash, MurmurHash3, and RapidHash. These are speed-first hashes: fast and well-distributed against random input, where the only "adversary" is randomness, not a person who controls the bytes. For digests, signatures, or HMAC, use the crypto extension instead.
What it is
Hashfuncs gives you fast, deterministic 32-, 64-, and 128-bit hashes β the slot between DuckDB's built-in hash() (great for join/group-by internals, but implementation-defined and unsafe to persist or reproduce elsewhere) and a cryptographic hash (an order of magnitude slower, and unnecessary when nothing adversarial is at stake). The natural workloads are partitioning, sharding, deduplication, cache keys, and probabilistic-filter inputs β anywhere you want a stable, well-distributed value derived from data at near-RAM bandwidth.
Hashfuncs vs. crypto β picking the right extension
These two extensions look similar but solve different problems. Pick by the threat model, not by output width.
-
β’
Use hashfuncs when speed wins: Partitioning, sharding, deduplication, cache keys, Bloom-filter inputs β workloads where the adversary is randomness, not a human.
xxh3_64andrapidhashrun roughly an order of magnitude faster than SHA-256. - β’ Use crypto when collisions must be hard to forge: Digital signatures, HMAC, content-addressed storage with adversarial input, tamper-evident audit logs, password salts, secure random bytes β anything an attacker could try to break. The crypto extension gives you BLAKE3, SHA-2, SHA-3, HMAC, and a CSPRNG.
-
β’
Don't substitute one for the other: These hashes are explicitly not collision-resistant against a motivated adversary. Treating an
xxh3_128like a SHA-256 digest is a security bug, not a performance optimization. The inverse is also wrong: spending SHA-256 on partition routing is wasted CPU. (For password storage, neither family is the answer β use a real password hash like bcrypt or argon2 outside DuckDB.) -
β’
They compose: Both extensions can be loaded together. A common pattern:
cryptofor the durable identity hash on a row,hashfuncsfor the routing hash that decides which shard processes it.
How it works
Every function is a scalar SQL function with one or two arguments β the value, and an optional seed. Each family is an upstream-faithful C/C++ implementation linked into the extension binary: no catalog, no secrets, no network, every call pure CPU.
- β’ Three families, one interface: xxHash (32 / 64 / 128-bit, including a hex digest; XXH3 variants are tuned for SSE2 / AVX2 / NEON), MurmurHash3 (32-bit plus x86 and x64 128-bit forms), and RapidHash, a newer 64-bit hash that often wins published throughput benchmarks. Every function accepts any DuckDB scalar value as input.
- β’ Optional seeds for independent hash spaces: Every function has a seeded overload. Two different seeds give two uncorrelated hash spaces from the same column β exactly what you need for double-hashing in Bloom filters or running two parallel shardings whose bucket assignments must not correlate.
-
β’
Canonical hex digest for cross-language reproducibility: The 128-bit xxHash variant can emit the lowercase 32-character canonical hex form defined in the xxHash specification, in
low64 || high64byte order. That digest matches what Python xxhash, xxhash-rust, andXXH128_canonicalFromHashin C produce over the same bytes β so a fingerprint round-trips across SQL and application code. -
β’
Feeds the bitfilters extension directly: The 64-bit
UBIGINT-returning hashes (xxh3_64,rapidhash) are the natural input to the bitfilters extension's Bloom, XOR, Quotient, and Binary Fuse filters β sub-microsecond probabilistic set-membership built entirely in one DuckDB session.
Deep Dive
Technical Details
Magic moment β partition a billion rows in one query
Take any column, hit it with a fast hash, modulo the partition count, and you have a stable, well-distributed bucket assignment with zero configuration:
-- Bucket every event into one of 64 shards using xxHash3SELECT event_id, payload, xxh3_64(event_id) % 64 AS shardFROM events;xxh3_64 runs at near-RAM bandwidth on modern CPUs β typically an order of magnitude faster than a cryptographic hash. For workloads where the only βadversaryβ is randomness β partitioning, sharding, deduplication, Bloom filter inputs, cache keys β thatβs the right trade.
Every function in this extension is a non-cryptographic hash. They are fast and well-distributed against random inputs, but they do not resist a motivated adversary who controls the input. Treating xxh3_128 like a SHA-256 digest is a security bug, not a performance optimization.
For digital signatures, HMAC, content-addressed storage with adversarial input, password salts, tamper-evident audit logs, or secure random bytes, use the crypto extension instead β it provides BLAKE3, SHA-2, SHA-3, HMAC, and a CSPRNG.
Architecture
Hashfuncs is a small, focused extension: a set of scalar SQL functions, each a thin wrapper around an upstream-faithful C/C++ implementation linked into the extension binary. There is no catalog, no secrets, no network β every call is pure CPU.
Three families ship in the box:
- xxHash β Yann Colletβs family.
xxh32andxxh64are the original 32- and 64-bit forms;xxh3_64andxxh3_128are the modern XXH3 variants tuned for SSE2 / AVX2 / NEON.xxh3_128_hexemits the canonical 32-character hex digest defined by the xxHash specification. - MurmurHash3 β Austin Applebyβs classic. Three forms are exposed:
murmurhash3_32,murmurhash3_128(x86 variant), andmurmurhash3_x64_128(x64 variant). The x64 form is what Apache CassandraβsMurmur3Partitionerand Apache Sparkβs default hash partitioner use. - RapidHash β a newer 64-bit hash optimized purely for throughput. Exposed as
rapidhash; generally wins published benchmarks against XXH3_64 on x86_64.
Every function accepts any DuckDB scalar value and (optionally) a seed. Two different seeds produce two uncorrelated hash spaces from the same input β useful for double-hashing in Bloom filters or running parallel shardings without correlation.
Hashfuncs vs. crypto β picking the right extension
Both extensions output deterministic hashes, but they answer different questions.
| You need⦠| Reach for | Why |
|---|---|---|
| Partitioning / sharding | xxh3_64, murmurhash3_x64_128 |
Speed; well-distributed for random input |
| Bloom / XOR / Cuckoo filter input | xxh3_64, rapidhash |
UBIGINT output, near-RAM bandwidth |
| Cross-language fingerprint | xxh3_128_hex |
Canonical hex; matches Python / Rust / C |
| Cassandra / Spark interop | murmurhash3_x64_128 |
Standard hash for those routers |
| Cheap deduplication | xxh3_64 or xxh3_128 |
64 vs 128 bits = collision-safety vs speed |
| Digital signatures, HMAC | crypto |
Adversarial collision resistance |
| Tamper-evident logs / chains | crypto |
SHA-2 / SHA-3 / BLAKE3 |
| Salts, keys, nonces (CSPRNG) | crypto |
crypto_random_bytes from OpenSSL |
| Password storage | Neither β use a real password hash (bcrypt / argon2) outside DuckDB | These are general-purpose hashes |
The two extensions compose. A common pattern is a crypto digest as the durable identity hash on a row, and an xxh3_64 hash for the routing decision that picks which shard processes it.
Compared to alternatives
vs. DuckDBβs built-in hash(): hash() is non-cryptographic too and is great for join/group-by internals. But its algorithm is implementation-defined and may change between DuckDB versions β donβt persist its output, and donβt expect another tool to reproduce it. Hashfuncs gives you named, stable algorithms whose output other systems can compute identically.
vs. cryptographic hashes (SHA-256, BLAKE3): roughly an order of magnitude slower per byte, but they resist adversarial collisions. Use crypto when that property matters; donβt pay for it when it doesnβt.
vs. doing this in Python / Pandas: pulling the column out of DuckDB just to hash it usually costs more than the hash itself. xxh3_64(...) in SQL is one vectorized scan with no Python round-trip β the same algorithm xxhash would compute, but in-process.
Seeds and overloads
Every function has both an unseeded form and a seeded form. The seed type follows the hash width:
- 32-bit functions (
xxh32,murmurhash3_32,murmurhash3_128) take aUINTEGERseed. - 64-bit and 128-bit xxHash variants (
xxh64,xxh3_64,xxh3_128,xxh3_128_hex) andrapidhashtake aUBIGINTseed. murmurhash3_x64_128takes aUINTEGERseed (matching the upstream MurmurHash3 API).
Seeded variants are the right tool whenever you want the same column to participate in two different hash spaces β for example, a Bloom filter that uses two independent hash functions, or two independent shardings with no correlation between bucket assignments.
Pairing with bitfilters
The bitfilters extension (Bloom, XOR, Quotient, and Binary Fuse filters) takes UBIGINT hashes as its input. Hashfuncs is the natural producer:
-- Build an xor8 filter from a column of stringsSELECT xor8_filter(xxh3_64(email)) AS filterFROM allowed_users;-- Test membership in a streaming querySELECT *FROM events e, filter_table fWHERE xor8_filter_contains(f.filter, xxh3_64(e.email));The combination gives you sub-microsecond probabilistic set-membership at SQL speed β INSTALL hashfuncs FROM community; INSTALL bitfilters FROM community; and you have everything in one DuckDB session.
Install
INSTALL hashfuncs FROM community;
LOAD hashfuncs;
Quick Start
64-bit xxHash3 β recommended general-purpose default
SELECT xxh3_64(payload) AS h FROM events;
Seeded for reproducible partitioning
SELECT xxh3_64(user_id, 42) % 16 AS partition FROM users;
128-bit hex digest (canonical xxhash byte order; matches Python xxhash.xxh3_128().hexdigest())
SELECT xxh3_128_hex('hello') AS digest;
Reference
Extension Contents
Quick reference to all available functions and settings organized by category.
| Name | Type | Description |
|---|---|---|
|
MurmurHash3
Austin Appleby's MurmurHash3 β a battle-tested non-cryptographic hash widely used in distributed systems including Apache Cassandra, Apache Spark, and many partitioner implementations. Choose |
||
| murmurhash3_128() | 128-bit MurmurHash3 β the x86 variant. | |
| murmurhash3_32() | 32-bit MurmurHash3. | |
| murmurhash3_x64_128() | 128-bit MurmurHash3 β the x64 variant. | |
|
RapidHash
RapidHash is purpose-built for raw 64-bit throughput while keeping competitive distribution quality. Frequently the fastest published 64-bit non-cryptographic hash on x86_64 β reach for |
||
| rapidhash() | RapidHash β a 64-bit non-cryptographic hash designed for exceptional speed while keeping competitive distribution quality. | |
|
xxHash
Yann Collet's xxHash family β among the fastest non-cryptographic hashes available, with excellent distribution. |
||
| xxh3_128() |
128-bit xxHash3 (XXH3_128) returned as a UHUGEINT.
|
|
| xxh3_128_hex() |
128-bit xxHash3 returned as a 32-character lowercase hex string in canonical xxHash byte order (low64 || high64), as defined by the xxHash specification.
|
|
| xxh3_64() | 64-bit xxHash3 (XXH3_64) β the modern xxHash variant tuned for vectorized CPUs (SSE2/AVX2). | |
| xxh32() | 32-bit xxHash (XXH32) β the classic 32-bit member of the xxHash family. | |
| xxh64() | 64-bit xxHash (XXH64). | |
No extension contents match that search.
API Reference
Function Documentation
Practical Examples
Cookbook
Real-world recipes and patterns for common use cases.
Pick the right function
| You want⦠| Pick |
|---|---|
| A modern fast 64-bit default | xxh3_64 |
| Maximum throughput | rapidhash |
| Cross-system compatibility (Cassandra / Spark) | murmurhash3_x64_128 |
| Stable hex digest matching Python / Rust xxhash | xxh3_128_hex |
| 128-bit space for billions of distinct keys | xxh3_128 or murmurhash3_x64_128 |
| Cryptographic guarantees | not this extension β use crypto |
For digests, signatures, HMAC, secure random, or anywhere an attacker controls the input, reach for crypto. None of the functions in this extension are designed to resist adversarial collisions.
Hash partitioning
Pick a partition by taking the hash modulo the partition count:
SELECT user_id, xxh3_64(user_id) % 16 AS partitionFROM users;A seed gives you a separate, uncorrelated hash space β useful when you want two different shardings of the same column without correlation between bucket assignments:
SELECT user_id, xxh3_64(user_id, 1) % 16 AS shard_a, xxh3_64(user_id, 2) % 16 AS shard_bFROM users;Materialize the result with CREATE TABLE AS to write a partitioned Parquet dataset.
Cross-system routing (Cassandra / Spark)
Use murmurhash3_x64_128 when DuckDB needs to compute the same routing hash that an upstream Apache Cassandra Murmur3Partitioner or Apache Spark job would assign:
-- Same hash Cassandra's Murmur3Partitioner would computeSELECT key, murmurhash3_x64_128(key) AS tokenFROM rows_to_route;String cache keys
xxh3_128_hex emits the canonical 32-character hex digest used by Python xxhash and xxhash-rust β the right pick when the key needs to be a string (Redis key, filename, URL component):
-- Stable cache key for a (user, query) tupleSELECT user_id, query_text, xxh3_128_hex( CAST(user_id AS VARCHAR) || '|' || query_text ) AS cache_keyFROM query_log;Bloom / XOR / Cuckoo filter input
Pair with the bitfilters extension. Most filter constructors take UBIGINT hashes β exactly what xxh3_64 and rapidhash produce:
-- Build an xor8 filter from a column of stringsSELECT xor8_filter(xxh3_64(email)) AS filterFROM allowed_users;-- Test membershipSELECT xor8_filter_contains(f.filter, xxh3_64(:candidate_email)) AS might_be_allowedFROM filter_table f;For a Bloom filter using two independent hash functions, use two seeds of the same algorithm rather than two different algorithms:
SELECT xxh3_64(key, 1) AS h1, xxh3_64(key, 2) AS h2FROM data;Cross-language fingerprints
xxh3_128_hex matches the canonical lowercase 32-character hex form (low64 || high64) defined by the xxHash specification. The same bytes hashed in any of these will produce the same digest:
SELECT xxh3_128_hex('hello');-- c779cfaa5e523818b5e9c1ad071b3e7f# Python β pip install xxhash (https://pypi.org/project/xxhash/)import xxhashxxhash.xxh3_128('hello').hexdigest()# 'c779cfaa5e523818b5e9c1ad071b3e7f'// Rust β xxhash-rust (https://docs.rs/xxhash-rust/latest/xxhash_rust/)use xxhash_rust::xxh3::xxh3_128;format!("{:032x}", xxh3_128(b"hello").swap_bytes());Useful as a content fingerprint shared between SQL and application code.
Cheap deduplication
SELECT DISTINCT xxh3_64(...) collapses duplicate payloads at near-RAM bandwidth. Drop to 128 bits when the keyspace is large enough that 64-bit birthday collisions matter:
-- Distinct payloads in a high-volume event streamSELECT DISTINCT xxh3_64(payload) AS payload_hashFROM events;-- Same idea with more headroomSELECT DISTINCT xxh3_128(payload) AS payload_hashFROM events;Treat the hash as an accidental-collision-safe key, not a security claim.
Independent hashes via seeds
Two different seeds give two uncorrelated hashes from the same input. Useful for double-hashing schemes, parallel shardings, and probabilistic data structures that need multiple hash functions:
SELECT key, xxh3_64(key, 1) AS h1, xxh3_64(key, 2) AS h2, xxh3_64(key, 3) AS h3FROM keys;Platform Support
Compatibility
Extension availability may vary by platform and DuckDB version. Check below to ensure this extension supports your environment before installation.
Quick Facts
Platforms
- Linux x86_64 aarch64
- Linux (musl) Not available
- macOS Intel Apple Silicon
- Windows x86_64
- WASM eh mvp threads
Compiled binary sizes
| Platform | Architecture | Size |
|---|---|---|
| Linux | x86_64 | 3.24 MB |
| Linux | aarch64 | 2.88 MB |
| macOS | Intel | 1.49 MB |
| macOS | Apple Silicon | 1.35 MB |
| Windows | x86_64 | 7.40 MB |
| WASM | eh | 33.9 KB |
| WASM | mvp | 28.4 KB |
| WASM | threads | 46.5 KB |
Compressed download size from the Haybarn extension repository.
DuckDB & Haybarn
Release calendar- DuckDB v1.5.5 Haybarn 1.5.5-rc1 Supported