Skip to main content
Glama

Redis vs ioredis vs valkey-glide

Written by on .

benchmark
Redis
valkey

  1. Benchmark Results
    1. Basic Operations
      1. Large Values (10KB)
        1. Hash Operations
          1. Pipeline/Transaction & Increment
            1. Concurrent Operations (100 parallel SETs)
              1. Summary
              2. Key Takeaways
                1. Sequential Operations
                  1. Pipeline vs Transaction (100 batched SETs)
                    1. Concurrent Operations (100 parallel SETs)
                      1. Bottom Line
                        1. Which Should You Pick?
                        2. Script

                          I benchmarked all major Node.js Redis clients–ioredis, redis, and valkey-glide–to see if we should stick with ioredis or if there are benefits to migrating to any of the other clients.

                          TL;DR: redis with RESP3 slightly edges out ioredis in most benchmarks. For typical web app workloads, the differences are negligible–pick based on API preference.

                          Here's the breakdown and the script I used.

                          Benchmark Results

                          Basic Operations

                          Operation

                          Client

                          Ops/sec

                          Avg

                          Min

                          Max

                          SET

                          ioredis

                          8,292

                          0.121ms

                          0.070ms

                          7.095ms

                          SET

                          redis

                          8,322

                          0.120ms

                          0.072ms

                          3.043ms

                          SET

                          redis (RESP3)

                          8,139

                          0.123ms

                          0.068ms

                          2.270ms

                          SET

                          valkey-glide

                          6,764

                          0.148ms

                          0.101ms

                          9.287ms

                          GET

                          ioredis

                          8,289

                          0.121ms

                          0.066ms

                          4.210ms

                          GET

                          redis

                          8,234

                          0.121ms

                          0.093ms

                          1.126ms

                          GET

                          redis (RESP3)

                          8,417

                          0.119ms

                          0.090ms

                          0.334ms

                          GET

                          valkey-glide

                          7,009

                          0.143ms

                          0.080ms

                          6.938ms

                          Large Values (10KB)

                          Operation

                          Client

                          Ops/sec

                          Avg

                          Min

                          Max

                          SET

                          ioredis

                          5,414

                          0.185ms

                          0.125ms

                          0.513ms

                          SET

                          redis

                          4,997

                          0.200ms

                          0.126ms

                          31.893ms

                          SET

                          redis (RESP3)

                          5,363

                          0.186ms

                          0.136ms

                          0.412ms

                          SET

                          valkey-glide

                          4,583

                          0.218ms

                          0.143ms

                          9.883ms

                          GET

                          ioredis

                          8,114

                          0.123ms

                          0.087ms

                          2.452ms

                          GET

                          redis

                          7,955

                          0.126ms

                          0.081ms

                          3.055ms

                          GET

                          redis (RESP3)

                          7,979

                          0.125ms

                          0.091ms

                          0.955ms

                          GET

                          valkey-glide

                          7,486

                          0.134ms

                          0.087ms

                          0.463ms

                          Hash Operations

                          Operation

                          Client

                          Ops/sec

                          Avg

                          Min

                          Max

                          HSET

                          ioredis

                          8,190

                          0.122ms

                          0.080ms

                          10.014ms

                          HSET

                          redis

                          8,500

                          0.118ms

                          0.086ms

                          0.420ms

                          HSET

                          redis (RESP3)

                          9,073

                          0.110ms

                          0.081ms

                          0.367ms

                          HSET

                          valkey-glide

                          7,152

                          0.140ms

                          0.105ms

                          0.383ms

                          HGET

                          ioredis

                          7,846

                          0.127ms

                          0.075ms

                          6.775ms

                          HGET

                          redis

                          8,331

                          0.120ms

                          0.077ms

                          0.692ms

                          HGET

                          redis (RESP3)

                          8,475

                          0.118ms

                          0.066ms

                          3.579ms

                          HGET

                          valkey-glide

                          6,926

                          0.144ms

                          0.101ms

                          4.106ms

                          Pipeline/Transaction & Increment

                          Operation

                          Client

                          Ops/sec

                          Avg

                          Min

                          Max

                          Pipeline (100 SETs)

                          ioredis pipeline

                          3,447

                          0.290ms

                          0.252ms

                          0.567ms

                          Transaction (100 SETs)

                          ioredis multi

                          3,249

                          0.308ms

                          0.265ms

                          0.734ms

                          Transaction (100 SETs)

                          redis multi

                          3,588

                          0.279ms

                          0.241ms

                          0.403ms

                          Transaction (100 SETs)

                          redis (RESP3) multi

                          3,707

                          0.270ms

                          0.245ms

                          0.430ms

                          Sequential (100 SETs)

                          valkey-glide*

                          72

                          13.825ms

                          9.756ms

                          15.095ms

                          INCR

                          ioredis

                          7,721

                          0.129ms

                          0.078ms

                          24.765ms

                          INCR

                          redis

                          8,505

                          0.118ms

                          0.075ms

                          0.440ms

                          INCR

                          redis (RESP3)

                          8,181

                          0.122ms

                          0.081ms

                          10.688ms

                          INCR

                          valkey-glide

                          7,025

                          0.142ms

                          0.087ms

                          4.785ms

                          NOTE

                          valkey-glide has no pipeline/transaction support–tested with sequential operations

                          Concurrent Operations (100 parallel SETs)

                          Client

                          Ops/sec

                          Avg

                          Min

                          Max

                          ioredis

                          2,342

                          0.427ms

                          0.323ms

                          2.850ms

                          ioredis (auto-pipeline)

                          3,343

                          0.299ms

                          0.268ms

                          0.359ms

                          redis

                          3,639

                          0.275ms

                          0.232ms

                          0.496ms

                          redis (RESP3)

                          3,690

                          0.271ms

                          0.221ms

                          1.626ms

                          redis (pool)

                          260

                          3.841ms

                          3.629ms

                          4.841ms

                          valkey-glide

                          2,113

                          0.473ms

                          0.405ms

                          0.668ms

                          Summary

                          Operation

                          ioredis

                          redis

                          redis (RESP3)

                          valkey-glide

                          Winner

                          SET

                          8,292

                          8,322

                          8,139

                          6,764

                          redis

                          GET

                          8,289

                          8,234

                          8,417

                          7,009

                          redis (RESP3)

                          SET (10KB)

                          5,414

                          4,997

                          5,363

                          4,583

                          ioredis

                          GET (10KB)

                          8,114

                          7,955

                          7,979

                          7,486

                          ioredis

                          HSET

                          8,190

                          8,500

                          9,073

                          7,152

                          redis (RESP3)

                          HGET

                          7,846

                          8,331

                          8,475

                          6,926

                          redis (RESP3)

                          Pipeline

                          3,447

                          ioredis

                          Transaction

                          3,249

                          3,588

                          3,707

                          redis (RESP3)

                          INCR

                          7,721

                          8,505

                          8,181

                          7,025

                          redis

                          Concurrent

                          2,342

                          3,639

                          3,690

                          2,113

                          redis (RESP3)

                          Key Takeaways

                          Sequential Operations

                          Comparison

                          Result

                          ioredis vs redis

                          Roughly equal (±3%)

                          redis (RESP3) vs redis

                          RESP3 slightly faster for hash ops

                          ioredis vs valkey-glide

                          ioredis ~15-20% faster

                          Pipeline vs Transaction (100 batched SETs)

                          Comparison

                          Result

                          ioredis pipeline vs ioredis multi

                          Pipeline ~6% faster (no atomicity)

                          redis (RESP3) multi vs ioredis multi

                          redis (RESP3) 14% faster

                          valkey-glide

                          ⚠️ No pipeline/transaction support (48x slower)

                          Concurrent Operations (100 parallel SETs)

                          Comparison

                          Result

                          redis (RESP3) vs ioredis

                          redis (RESP3) 57% faster

                          redis (RESP3) vs ioredis (auto-pipeline)

                          redis (RESP3) 10% faster

                          ioredis auto-pipeline vs standard

                          auto-pipeline 43% faster

                          redis pool

                          ⚠️ Performed poorly (avoid)

                          Bottom Line

                          • Sequential workloads → All clients perform similarly (±5%)

                          • Concurrent workloadsredis wins; ioredis with auto-pipeline closes the gap

                          • Transactionsredis is now faster (14% with RESP3)

                          • Large value writes → ioredis has a slight edge

                          • Batch operations → Avoid valkey-glide (no pipeline/transaction support)

                          • RESP3 → Provides modest gains for redis, especially for hash operations

                          Which Should You Pick?

                          Use Case

                          Recommendation

                          High-traffic web app

                          redis (RESP3) or ioredis (auto-pipeline)

                          Bulk/batch operations

                          ioredis (pipeline support)

                          Simple, low-traffic app

                          Either–differences won't matter

                          Need Valkey compatibility

                          valkey-glide (but expect performance tradeoffs)

                          Script

                          import { config } from '#app/config.server.ts'; import { GlideClient } from '@valkey/valkey-glide'; import Redis from 'ioredis'; import { createClient, createClientPool } from 'redis'; /** * Benchmarks Redis vs Valkey client performance. * * Run with: * ``` * node --expose-gc --import tsx app/bin/benchmark-redis.ts * ``` */ const maybeGc = () => { if (global.gc) { global.gc(); } }; const parseRedisUrl = ( url: string, ): { host: string; port: number; } => { const parsed = new URL(url); return { host: parsed.hostname, port: parsed.port ? Number(parsed.port) : 6_379, }; }; type BenchmarkResult = { avgMs: number; maxMs: number; minMs: number; name: string; opsPerSec: number; totalMs: number; }; const runBenchmark = async ( name: string, iterations: number, fn: () => Promise<void>, ): Promise<BenchmarkResult> => { const times: number[] = []; // Warmup for (let i = 0; i < Math.min(100, iterations / 10); i++) { await fn(); } const start = performance.now(); for (let i = 0; i < iterations; i++) { const iterStart = performance.now(); await fn(); times.push(performance.now() - iterStart); } const totalMs = performance.now() - start; return { avgMs: times.reduce((a, b) => a + b, 0) / times.length, maxMs: Math.max(...times), minMs: Math.min(...times), name, opsPerSec: Math.round((iterations / totalMs) * 1_000), totalMs, }; }; const formatResult = (result: BenchmarkResult): string => { return `${result.name}: ${result.opsPerSec.toLocaleString()} ops/sec (avg: ${result.avgMs.toFixed(3)}ms, min: ${result.minMs.toFixed(3)}ms, max: ${result.maxMs.toFixed(3)}ms)`; }; const main = async () => { const { host, port } = parseRedisUrl(config.REDIS_DSN); console.log(`Connecting to Redis at ${host}:${port}\n`); // Initialize clients const ioredisClient = new Redis({ host, port }); const ioredisAutoPipelineClient = new Redis({ enableAutoPipelining: true, host, port, }); const redisClient = createClient({ url: `redis://${host}:${port}` }); await redisClient.connect(); const redisResp3Client = createClient({ RESP: 3, url: `redis://${host}:${port}`, }); await redisResp3Client.connect(); const redisPoolClient = createClientPool( { url: `redis://${host}:${port}` }, { maximum: 5, minimum: 5 }, ); await redisPoolClient.connect(); const glideClient = await GlideClient.createClient({ addresses: [{ host, port }], clientName: 'benchmark', }); const iterations = 10_000; const testKey = 'benchmark:string'; const hashKey = 'benchmark:hash'; const counterKey = 'benchmark:counter'; const pipelineKeyPrefix = 'benchmark:pipeline'; const concurrentKeyPrefix = 'benchmark:concurrent'; const testValue = 'hello world'; const largeValue = 'x'.repeat(10_000); console.log( `Running benchmarks with ${iterations.toLocaleString()} iterations each\n`, ); // =================== // SEQUENTIAL BENCHMARKS // These run one operation at a time - tests raw client overhead // =================== maybeGc(); // SET benchmarks console.log('=== SET (sequential) ==='); console.log( formatResult( await runBenchmark('ioredis', iterations, async () => { await ioredisClient.set(testKey, testValue); }), ), ); console.log( formatResult( await runBenchmark('redis', iterations, async () => { await redisClient.set(testKey, testValue); }), ), ); console.log( formatResult( await runBenchmark('redis (RESP3)', iterations, async () => { await redisResp3Client.set(testKey, testValue); }), ), ); console.log( formatResult( await runBenchmark('valkey-glide', iterations, async () => { await glideClient.set(testKey, testValue); }), ), ); maybeGc(); // GET benchmarks console.log('\n=== GET (sequential) ==='); console.log( formatResult( await runBenchmark('ioredis', iterations, async () => { await ioredisClient.get(testKey); }), ), ); console.log( formatResult( await runBenchmark('redis', iterations, async () => { await redisClient.get(testKey); }), ), ); console.log( formatResult( await runBenchmark('redis (RESP3)', iterations, async () => { await redisResp3Client.get(testKey); }), ), ); console.log( formatResult( await runBenchmark('valkey-glide', iterations, async () => { await glideClient.get(testKey); }), ), ); maybeGc(); // Large value SET benchmarks console.log('\n=== SET 10KB (sequential) ==='); console.log( formatResult( await runBenchmark('ioredis', iterations, async () => { await ioredisClient.set(testKey, largeValue); }), ), ); console.log( formatResult( await runBenchmark('redis', iterations, async () => { await redisClient.set(testKey, largeValue); }), ), ); console.log( formatResult( await runBenchmark('redis (RESP3)', iterations, async () => { await redisResp3Client.set(testKey, largeValue); }), ), ); console.log( formatResult( await runBenchmark('valkey-glide', iterations, async () => { await glideClient.set(testKey, largeValue); }), ), ); maybeGc(); // Large value GET benchmarks console.log('\n=== GET 10KB (sequential) ==='); console.log( formatResult( await runBenchmark('ioredis', iterations, async () => { await ioredisClient.get(testKey); }), ), ); console.log( formatResult( await runBenchmark('redis', iterations, async () => { await redisClient.get(testKey); }), ), ); console.log( formatResult( await runBenchmark('redis (RESP3)', iterations, async () => { await redisResp3Client.get(testKey); }), ), ); console.log( formatResult( await runBenchmark('valkey-glide', iterations, async () => { await glideClient.get(testKey); }), ), ); maybeGc(); // HSET benchmarks console.log('\n=== HSET (sequential) ==='); console.log( formatResult( await runBenchmark('ioredis', iterations, async () => { await ioredisClient.hset(hashKey, 'field', testValue); }), ), ); console.log( formatResult( await runBenchmark('redis', iterations, async () => { await redisClient.hSet(hashKey, 'field', testValue); }), ), ); console.log( formatResult( await runBenchmark('redis (RESP3)', iterations, async () => { await redisResp3Client.hSet(hashKey, 'field', testValue); }), ), ); console.log( formatResult( await runBenchmark('valkey-glide', iterations, async () => { await glideClient.hset(hashKey, { field: testValue }); }), ), ); maybeGc(); // HGET benchmarks console.log('\n=== HGET (sequential) ==='); console.log( formatResult( await runBenchmark('ioredis', iterations, async () => { await ioredisClient.hget(hashKey, 'field'); }), ), ); console.log( formatResult( await runBenchmark('redis', iterations, async () => { await redisClient.hGet(hashKey, 'field'); }), ), ); console.log( formatResult( await runBenchmark('redis (RESP3)', iterations, async () => { await redisResp3Client.hGet(hashKey, 'field'); }), ), ); console.log( formatResult( await runBenchmark('valkey-glide', iterations, async () => { await glideClient.hget(hashKey, 'field'); }), ), ); maybeGc(); // INCR benchmarks console.log('\n=== INCR (sequential) ==='); await ioredisClient.set(counterKey, '0'); console.log( formatResult( await runBenchmark('ioredis', iterations, async () => { await ioredisClient.incr(counterKey); }), ), ); console.log( formatResult( await runBenchmark('redis', iterations, async () => { await redisClient.incr(counterKey); }), ), ); console.log( formatResult( await runBenchmark('redis (RESP3)', iterations, async () => { await redisResp3Client.incr(counterKey); }), ), ); console.log( formatResult( await runBenchmark('valkey-glide', iterations, async () => { await glideClient.incr(counterKey); }), ), ); // =================== // PIPELINE/TRANSACTION BENCHMARKS // Note: ioredis pipeline() vs redis multi() are NOT equivalent: // - pipeline(): Batches commands, no atomicity (faster) // - multi(): Transaction with MULTI/EXEC atomicity (slower) // redis v4+ lacks a direct pipeline equivalent // =================== maybeGc(); const pipelineSize = 100; const pipelineIterations = iterations / pipelineSize; console.log( `\n=== Pipeline/Transaction (${pipelineSize} SETs per batch) ===`, ); // ioredis pipeline - batches without atomicity console.log( formatResult( await runBenchmark( 'ioredis pipeline (no atomicity)', pipelineIterations, async () => { const pipeline = ioredisClient.pipeline(); for (let i = 0; i < pipelineSize; i++) { pipeline.set(`${pipelineKeyPrefix}:${i}`, testValue); } await pipeline.exec(); }, ), ), ); // ioredis multi - transaction with atomicity (for fair comparison with redis) console.log( formatResult( await runBenchmark( 'ioredis multi (atomic)', pipelineIterations, async () => { const multi = ioredisClient.multi(); for (let i = 0; i < pipelineSize; i++) { multi.set(`${pipelineKeyPrefix}:${i}`, testValue); } await multi.exec(); }, ), ), ); // redis multi - transaction with atomicity console.log( formatResult( await runBenchmark( 'redis multi (atomic)', pipelineIterations, async () => { const multi = redisClient.multi(); for (let i = 0; i < pipelineSize; i++) { multi.set(`${pipelineKeyPrefix}:${i}`, testValue); } await multi.exec(); }, ), ), ); // redis RESP3 multi - transaction with atomicity console.log( formatResult( await runBenchmark( 'redis (RESP3) multi (atomic)', pipelineIterations, async () => { const multi = redisResp3Client.multi(); for (let i = 0; i < pipelineSize; i++) { multi.set(`${pipelineKeyPrefix}:${i}`, testValue); } await multi.exec(); }, ), ), ); // valkey-glide doesn't have pipeline console.log( formatResult( await runBenchmark( 'valkey-glide (no pipeline)', pipelineIterations, async () => { for (let i = 0; i < pipelineSize; i++) { await glideClient.set(`${pipelineKeyPrefix}:${i}`, testValue); } }, ), ), ); // =================== // CONCURRENT BENCHMARKS // Multiple operations in flight simultaneously - this is where // auto-pipelining and connection pools provide benefits // =================== maybeGc(); const concurrentSize = 100; const concurrentIterations = iterations / concurrentSize; console.log(`\n=== Concurrent (${concurrentSize} parallel SETs) ===`); // Single client - commands queue behind each other console.log( formatResult( await runBenchmark('ioredis', concurrentIterations, async () => { await Promise.all( Array.from( { length: concurrentSize }, async (_, i) => await ioredisClient.set(`${concurrentKeyPrefix}:${i}`, testValue), ), ); }), ), ); // Auto-pipeline batches concurrent commands automatically console.log( formatResult( await runBenchmark( 'ioredis (auto-pipeline)', concurrentIterations, async () => { await Promise.all( Array.from( { length: concurrentSize }, async (_, i) => await ioredisAutoPipelineClient.set( `${concurrentKeyPrefix}:${i}`, testValue, ), ), ); }, ), ), ); // Single client console.log( formatResult( await runBenchmark('redis', concurrentIterations, async () => { await Promise.all( Array.from({ length: concurrentSize }, (_, i) => redisClient.set(`${concurrentKeyPrefix}:${i}`, testValue), ), ); }), ), ); // Single client with RESP3 console.log( formatResult( await runBenchmark('redis (RESP3)', concurrentIterations, async () => { await Promise.all( Array.from({ length: concurrentSize }, (_, i) => redisResp3Client.set(`${concurrentKeyPrefix}:${i}`, testValue), ), ); }), ), ); // Pool distributes across 5 connections console.log( formatResult( await runBenchmark('redis (pool)', concurrentIterations, async () => { await Promise.all( Array.from({ length: concurrentSize }, (_, i) => redisPoolClient.set(`${concurrentKeyPrefix}:${i}`, testValue), ), ); }), ), ); console.log( formatResult( await runBenchmark('valkey-glide', concurrentIterations, async () => { await Promise.all( Array.from({ length: concurrentSize }, (_, i) => glideClient.set(`${concurrentKeyPrefix}:${i}`, testValue), ), ); }), ), ); // Cleanup console.log('\nCleaning up...'); await ioredisClient.del(testKey); await ioredisClient.del(hashKey); await ioredisClient.del(counterKey); for (let i = 0; i < pipelineSize; i++) { await ioredisClient.del(`${pipelineKeyPrefix}:${i}`); } for (let i = 0; i < concurrentSize; i++) { await ioredisClient.del(`${concurrentKeyPrefix}:${i}`); } console.log('Done!'); }; main().catch(console.error);

                          Written by punkpeye (@punkpeye)