Redis bitfields
Introduction to Redis bitfields
Redis bitfields let you set, increment, and get integer values of arbitrary bit length. For example, you can operate on anything from unsigned 1-bit integers to signed 63-bit integers.
These values are stored using binary-encoded Redis strings. Bitfields support atomic read, write and increment operations, making them a good choice for managing counters and similar numerical values.
Basic commands
BITFIELD
atomically sets, increments and reads one or more values.BITFIELD_RO
is a read-only variant ofBITFIELD
.
Example
Suppose you want to maintain two metrics for various bicycles: the current price and the number of owners over time. You can represent these counters with a 32-bit wide bitfield for each bike.
- Bike 1 initially costs 1,000 (counter in offset 0) and has never had an owner. After being sold, it's now considered used and the price instantly drops to reflect its new condition, and it now has an owner (offset 1). After quite some time, the bike becomes a classic. The original owner sells it for a profit, so the price goes up and the number of owners does as well.Finally, you can look at the bike's current price and number of owners.
> BITFIELD bike:1:stats SET u32 #0 1000
1) (integer) 0
> BITFIELD bike:1:stats INCRBY u32 #0 -50 INCRBY u32 #1 1
1) (integer) 950
2) (integer) 1
> BITFIELD bike:1:stats INCRBY u32 #0 500 INCRBY u32 #1 1
1) (integer) 1450
2) (integer) 2
> BITFIELD bike:1:stats GET u32 #0 GET u32 #1
1) (integer) 1450
2) (integer) 2
"""
Code samples for Bitfield doc pages:
https://redis.io/docs/latest/develop/data-types/bitfields/
"""
import redis
r = redis.Redis(decode_responses=True)
bf = r.bitfield("bike:1:stats")
res1 = bf.set("u32", "#0", 1000).execute()
print(res1) # >>> [0]
res2 = bf.incrby("u32", "#0", -50).incrby("u32", "#1", 1).execute()
print(res2) # >>> [950, 1]
res3 = bf.incrby("u32", "#0", 500).incrby("u32", "#1", 1).execute()
print(res3) # >>> [1450, 2]
res4 = bf.get("u32", "#0").get("u32", "#1").execute()
print(res4) # >>> [1450, 2]
import assert from 'assert';
import { createClient } from 'redis';
const client = createClient();
await client.connect();
let res1 = await client.bitField("bike:1:stats", [{
operation: 'SET',
encoding: 'u32',
offset: '#0',
value: 1000
}]);
console.log(res1); // >>> [0]
let res2 = await client.bitField('bike:1:stats', [
{
operation: 'INCRBY',
encoding: 'u32',
offset: '#0',
increment: -50
},
{
operation: 'INCRBY',
encoding: 'u32',
offset: '#1',
increment: 1
}
]);
console.log(res2); // >>> [950, 1]
let res3 = await client.bitField('bike:1:stats', [
{
operation: 'INCRBY',
encoding: 'u32',
offset: '#0',
increment: 500
},
{
operation: 'INCRBY',
encoding: 'u32',
offset: '#1',
increment: 1
}
]);
console.log(res3); // >>> [1450, 2]
let res4 = await client.bitField('bike:1:stats', [
{
operation: 'GET',
encoding: 'u32',
offset: '#0'
},
{
operation: 'GET',
encoding: 'u32',
offset: '#1'
}
]);
console.log(res4); // >>> [1450, 2]
import redis.clients.jedis.UnifiedJedis;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class BitfieldExample {
public void run() {
UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
List<Long> res1 = jedis.bitfield("bike:1:stats", "SET", "u32", "#0", "1000");
System.out.println(res1); // >>> [0]
List<Long> res2 = jedis.bitfield("bike:1:stats", "INCRBY", "u32", "#0", "-50", "INCRBY", "u32", "#1", "1");
System.out.println(res2); // >>> [950, 1]
List<Long> res3 = jedis.bitfield("bike:1:stats", "INCRBY", "u32", "#0", "500", "INCRBY", "u32", "#1", "1");
System.out.println(res3); // >>> [1450, 2]
List<Long> res4 = jedis.bitfield("bike:1:stats", "GET", "u32", "#0", "GET", "u32", "#1");
System.out.println(res4); // >>> [1450, 2]
// Tests for 'bf' step.
jedis.close();
}
}
package example_commands_test
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
func ExampleClient_bf() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password docs
DB: 0, // use default DB
})
res1, err := rdb.BitField(ctx, "bike:1:stats",
"set", "u32", "#0", "1000",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> [0]
res2, err := rdb.BitField(ctx,
"bike:1:stats",
"incrby", "u32", "#0", "-50",
"incrby", "u32", "#1", "1",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> [950 1]
res3, err := rdb.BitField(ctx,
"bike:1:stats",
"incrby", "u32", "#0", "500",
"incrby", "u32", "#1", "1",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> [1450 2]
res4, err := rdb.BitField(ctx, "bike:1:stats",
"get", "u32", "#0",
"get", "u32", "#1",
).Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> [1450 2]
}
Performance
BITFIELD
is O(n), where n is the number of counters accessed.