On this page
Technical Overview
Reach the cache without the app-layer glue
The usual path from DuckDB to Redis runs through app-layer Python or Node.js glue. This extension removes it: read and write a live instance from SQL. Best for operational queries, hot-cold data joins, and one-off migrations; for application-grade Redis access, keep a real client.
What it is
Redis is the most common in-memory cache and quick-lookup store, and it usually sits one network hop away from data you already keep in DuckDB. The normal way to bridge the two is to write a script โ pull keys out of Redis in Python or Node, reshape them, load them into a table. This extension removes that middle layer: it exposes Redis operations as redis_* SQL functions so the cache becomes just another source you can SELECT from, JOIN against, and write back to. It is scoped deliberately to the SQL-side workflows where data integration matters โ draining a keyspace into a table, joining cached lookups against bulk data, priming a cache from analytical output, auditing keys โ rather than to being a complete Redis client.
How it works
Connection details live in a DuckDB secret of TYPE redis, and every redis_* function takes a trailing argument naming that secret. This keeps credentials out of query text and lets dev / staging / prod live as separate secrets you select per query. There is no ATTACH interface and no virtual catalog โ this is purely a function-based extension, so Redis access composes inline anywhere a scalar function or table function is allowed.
-
โข
Secret-based configuration: A
redissecret holdshost,port, and an optionalpassword. SeeCREATE SECRET; parameterize the password from the environment so it never lands in a cached query plan. - โข RESP over TCP: The extension speaks the native Redis Serialization Protocol directly over a plain TCP socket โ no separate Redis daemon, sidecar, or driver process is involved.
-
โข
Connection reuse: Connections to a given secret are pooled and reused across calls rather than reopened per row, so per-row lookups inside a
SELECTand streaming table functions amortize the connection cost.
Production caveats
Be aware before pointing this at a busy Redis instance. The extension is honest-by-design about what it doesn't yet do.
-
โข
Use SCAN, not KEYS:
KEYSblocks the Redis server for the duration of the scan โ fine in dev, dangerous on production keyspaces. Prefer the cursor-paginatedSCAN-backed functions, which iterate without stalling traffic. - โข Strings, hashes, and lists only: No SET / ZSET / STREAM / PUB-SUB / TRANSACTIONS / SCRIPTING today. See Redis data types for what's supported elsewhere.
- โข Single-node connections: No Redis Cluster topology awareness. Point the secret at a single endpoint.
-
โข
Plaintext auth: The
passwordfield in the secret is sent over plain TCP โ TLS is not yet wired up. Use the extension on trusted networks until that lands. See Redis security for the upstream guidance. - โข Experimental status: The function surface may change as more Redis primitives land. Pin a known-good extension version in production CTAS jobs.
Deep Dive
Technical Details
What you can do with one query
The single most useful pattern: drain an entire Redis keyspace into a DuckDB table without writing a script:
CREATE TABLE redis_users_dump ASSELECT key, field, valueFROM redis_hscan_over_scan( /* scan_pattern */ 'user:*', /* hscan_pattern */ '*', /* count */ 100, /* secret */ 'redis');redis_hscan_over_scan chains the upstream SCAN and HSCAN cursor commands into one streaming (key, field, value) row source. Server-friendly cursor pagination throughout โ no KEYS blocking, no Python in the middle.
This extension is for SQL-side workflows where data integration matters more than every Redis feature. It supports strings, hashes, and lists; no SET / ZSET / STREAM / PUB-SUB / TRANSACTIONS / SCRIPTING. Connections are single-node (no Redis Cluster). Auth is currently plaintext โ TLS isnโt wired up yet. The status is experimental; the function surface may change.
For application-grade Redis access, keep a real client. For one-off migrations, hot-cold joins, and operational queries from SQL, this is the right tool.
Authentication
Every redis_* function takes a trailing secret argument naming a DuckDB secret of TYPE redis. This keeps credentials out of query text and lets multiple environments (dev/staging/prod) live as separate secrets โ pick the right one per query:
CREATE SECRET prod_redis ( TYPE redis, PROVIDER config, host 'cache.prod.internal', port '6379', password :prod_password);
SELECT redis_get('counter', 'prod_redis');See CREATE SECRET and the DuckDB Secrets Manager for the secrets infrastructure, and the Secrets section below for the parameter table the redis secret type accepts.
SCAN vs KEYS โ why it matters
redis_keys is the convenience function โ it returns all matching keys as a table. Itโs fine for development and small keyspaces, but it issues the KEYS command, which blocks the Redis server for the duration of the scan. On a large production instance this can stall traffic.
redis_scan is the production primitive โ cursor-paginated, non-blocking, server-side filtered. The SCAN protocol returns a cursor with each batch of keys; you re-pass the cursor to fetch the next page. Cursor 0 means iteration is complete:
SELECT redis_scan('0', 'user:*', 100, 'redis');-- "1024:user:1,user:2,...,user:50"redis_hscan_over_scan (used in the lead snippet above) chains both into one streaming pipeline: for every key matching scan_pattern, run HSCAN hscan_pattern and emit (key, field, value) rows. The fastest way to drain a Redis hash dataset into DuckDB.
Whatโs in the box
| Group | Functions | Redis commands |
|---|---|---|
| Strings | redis_get, redis_set, redis_mget |
GET, SET, MGET |
| Hashes | redis_hget, redis_hset, redis_hgetall, redis_hscan |
HGET, HSET, HGETALL, HSCAN |
| Lists | redis_lpush, redis_lrange, redis_lrange_table |
LPUSH, LRANGE |
| Discovery | redis_keys, redis_scan, redis_hscan_over_scan |
KEYS, SCAN, HSCAN |
| Key management | redis_type, redis_exists, redis_del |
TYPE, EXISTS, DEL |
The extension speaks the Redis Serialization Protocol over plain TCP. Recipes for each group live in the Cookbook.
Install
INSTALL redis FROM community;
LOAD redis;
Quick Start
Connect via a secret
CREATE SECRET IF NOT EXISTS redis (
TYPE redis,
PROVIDER config,
host 'localhost',
port '6379'
);
Read and write strings
CREATE SECRET IF NOT EXISTS redis (
TYPE redis,
PROVIDER config,
host 'localhost',
port '6379'
);
SELECT redis_set('user:1', 'John Doe', 'redis') AS ok;
SELECT redis_get('user:1', 'redis') AS name;
Discover keys matching a pattern
CREATE SECRET IF NOT EXISTS redis (
TYPE redis,
PROVIDER config,
host 'localhost',
port '6379'
);
SELECT * FROM redis_keys('user:*', 'redis');
Reference
Extension Contents
Quick reference to all available functions and settings organized by category.
| Name | Type | Description |
|---|---|---|
|
Cache / In-memory store
|
||
| redis | Redis connection details. | |
|
Hashes
HGET / HSET / HGETALL / HSCAN โ for field-keyed records stored under one Redis key. The |
||
| redis_hget() | Get the value of a hash field. | |
| redis_hgetall() | List all (field, value) pairs of a hash as a table. | |
| redis_hscan() | Cursor-paginated scan of a hash's fields. | |
| redis_hscan_over_scan() | For every key matching a SCAN pattern, run HSCAN and emit (key, field, value) rows. | |
| redis_hset() | Set the value of a hash field. | |
|
Keys
DEL / EXISTS / TYPE / SCAN / KEYS โ keyspace introspection and management. Use SCAN for production traffic; KEYS blocks the server on large keyspaces. |
||
| redis_del() | Delete a key. | |
| redis_exists() | Check whether a key exists. | |
| redis_keys() | List all keys matching a pattern as a table. | |
| redis_scan() | Cursor-paginated SCAN of the keyspace. | |
| redis_type() | Get the Redis type of a key (string / list / hash / set / zset / stream / none). | |
|
Lists
LPUSH / LRANGE โ interact with Redis lists. Use |
||
| redis_lpush() | Push a value onto the head of a list. | |
| redis_lrange() | Get a range from a list as a comma-separated string. | |
| redis_lrange_table() | Get a range from a list as a table โ one row per element. | |
|
Strings
GET / SET / MGET against Redis string keys. The simplest key-value layer โ read and write opaque values. |
||
| redis_get() | Get the value of a string key. | |
| redis_mget() | Get multiple values in one round-trip. | |
| redis_set() | Set the value of a string key. | |
No extension contents match that search.
API Reference
Function Documentation
Security
Secrets
DuckDB secrets for storing the credentials and keys used by the redis extension.
redis
Description
Redis connection details. All redis_* functions take a final secret argument naming a secret of this type.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| Parameter host | Type VARCHAR | Required Required | Description Redis server hostname or IP. |
| Parameter port | Type VARCHAR | Required Required | Description Redis server port (typically '6379'). |
| Parameter password | Type VARCHAR | Required Optional | Description Authentication password if the server requires AUTH. |
Examples
Local Redis without auth
CREATE SECRET IF NOT EXISTS redis (
TYPE redis,
PROVIDER config,
host 'localhost',
port '6379'
);
Redis Cloud with TLS endpoint
CREATE SECRET IF NOT EXISTS redis (
TYPE redis,
PROVIDER config,
host 'redis-1234.ec2.redns.redis-cloud.com',
port '16959',
password 'xxxxxx'
);
Practical Examples
Cookbook
Real-world recipes and patterns for common use cases.
Connect
Redis credentials live in a DuckDB secret. Every redis_* function takes a final secret argument naming this secret:
CREATE SECRET IF NOT EXISTS redis ( TYPE redis, PROVIDER config, host 'localhost', port '6379', password 'optional_password');For production, parameterize the password from the environment so it doesnโt appear in cached query plans:
SET VARIABLE redis_password = getenv('REDIS_PASSWORD');
CREATE SECRET prod_redis ( TYPE redis, PROVIDER config, host 'cache.prod.internal', port '6379', password :redis_password);See CREATE SECRET for the full secrets DSL and the Secrets section on this page for the redis parameter table.
String operations
-- Set a value (Redis SET)SELECT redis_set('user:1', 'John Doe', 'redis') AS ok;-- Get a value (Redis GET)SELECT redis_get('user:1', 'redis') AS user_name;-- Bulk write from a columnINSERT INTO users (id, name)SELECT id, redis_set('user:' || id::VARCHAR, name, 'redis')FROM new_users;-- Multi-get in one round-trip (Redis MGET)SELECT redis_mget('user:1,user:2,user:3', 'redis');Maps to GET, SET, MGET. See redis_get / redis_set / redis_mget for the function signatures.
Hash operations
-- Set fields (Redis HSET)SELECT redis_hset('user:1', 'age', '30', 'redis');-- Get one field (Redis HGET)SELECT redis_hget('user:1', 'email', 'redis') AS email;-- All fields and values as a table (Redis HGETALL)SELECT * FROM redis_hgetall('user:1', 'redis');Maps to HGET, HSET, HGETALL. See redis_hget / redis_hset / redis_hgetall.
List operations
-- Push values (Redis LPUSH)SELECT redis_lpush('queue:jobs', 'job-1', 'redis');SELECT redis_lpush('queue:jobs', 'job-2', 'redis');-- Read as a comma-joined stringSELECT redis_lrange('queue:jobs', 0, -1, 'redis');-- Or as a table โ one row per elementSELECT * FROM redis_lrange_table('queue:jobs', 0, -1, 'redis');Maps to LPUSH and LRANGE. redis_lrange_table is the table-shaped variant โ pick it when you want one DuckDB row per list element instead of a single comma-joined string from redis_lrange.
Discovery (SCAN, KEYS)
KEYS is convenient but blocks the Redis server on large keyspaces. In production, prefer cursor-paginated SCAN:
-- KEYS โ fine for development / small keyspacesSELECT * FROM redis_keys('user:*', 'redis');-- SCAN โ paginated, non-blockingSELECT redis_scan('0', 'user:*', 100, 'redis');-- โ "1024:user:1,user:2,...,user:50"-- Pass the returned cursor (1024) back in to fetch the next page; cursor "0" means done.See redis_keys and redis_scan.
Bulk hash discovery (redis_hscan_over_scan)
The killer feature for migration and bulk analysis โ combines SCAN with HSCAN to emit (key, field, value) rows for every hash matching a key pattern, in one query:
-- Every field of every user:* hash, as rowsSELECT key, field, valueFROM redis_hscan_over_scan( /* scan_pattern */ 'user:*', /* hscan_pattern */ '*', /* count */ 100, /* secret */ 'redis')ORDER BY key, field;Pipe directly into a CREATE TABLE AS for offline analysis or migration:
CREATE TABLE redis_users_dump ASSELECT key, field, valueFROM redis_hscan_over_scan('user:*', '*', 100, 'redis');See redis_hscan_over_scan for the full signature.
Key management
-- Type, existence, deletionSELECT redis_type('user:1', 'redis'); -- 'hash'SELECT redis_exists('user:1', 'redis'); -- TRUESELECT redis_del('user:1', 'redis'); -- TRUE if existed and removedMaps to TYPE, EXISTS, DEL. See redis_type / redis_exists / redis_del.
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 Not available
Compiled binary sizes
| Platform | Architecture | Size |
|---|---|---|
| Linux | x86_64 | 3.40 MB |
| Linux | aarch64 | 3.02 MB |
| macOS | Intel | 1.62 MB |
| macOS | Apple Silicon | 1.47 MB |
| Windows | x86_64 | 7.45 MB |
Compressed download size from the Haybarn extension repository.
DuckDB & Haybarn
Release calendar- DuckDB v1.5.5 Haybarn 1.5.5-rc1 Supported