Bitmaps are a pattern in redis, not actualy a data type as they are just using string, that can help save space when using redis. For example, you can store 4 billion users subscribed to a notification using only 512mb of memory with bitmaps. In this article, we will learn how to use Redis bitmaps in Nodejs.
For setting up Redis, I would recommend using a service for you in prod. Azure for example, has a great redis service that scales easily. However, you will want to learn redis and eventually how to scale it yourself. This will help with debugging cloud services or eventually, saving money and not using them.
We will start our intro to redis via using docker compose. Create a docker-compose.yml
file and add the following.
version: "3.2"
services:
redis:
image: "redis:alpine"
command: redis-server
ports:
- "6379:6379"
volumes:
- $PWD/redis-data:/var/lib/redis
- $PWD/redis.conf:/usr/local/etc/redis/redis.conf
environment:
- REDIS_REPLICATION_MODE=master
Ensure you have docker installed and run
docker-compose up
There are two modules I see often used in nodejs. I will tend towards
ioredis
as it has built in support for promises and many other features in redis.
npm install ioredis
Let's open up a new file, index.js
and go through many of
the common commands you will used with lists in redis.
To create a bit map, we can use the setbit
command. We provide the command with a name for the key, and offset or poistion in the bitmpa, and the value which must be 0 or 1. For example, we have want to track eight different users and say that user 7 is subscribed, we can use the following.
// Set Bit
await redis.setbit("users-subscribed", 7, 0)
Also note, that all unset bits will be set to 0. So the command above can represent 8 users where user 7 is on or 1 and other values will default to 0 for off.
We can retrive the bit information in two ways, the first is using the getbit
command, but we can also use the basic get
command as bitmaps are simply strings underneath.
// Get Bit
const result = await redis.getbit("users-subscribed", 7)
console.log(result) // 0
const result2 = await redis.get("users-subscribed")
console.log(result2) // 0
For each of the examples below, I will use the following template to run all the commands. Here is my full index file. We will just replace the commands each time.
const Redis = require("ioredis")
const redis = new Redis({})
async function main() {
// Set Bit
await redis.setbit("users-subscribed", 7, 1)
// Get Bit
const result = await redis.getbit("users-subscribed", 7)
console.log(result) // 0
}
(async () => {
await main()
})()
If we want to find the first bit turned off or on, we can use the bitpos
function. The following will tell use the first bit that is on, 1, in our key.
// Get Bit
const result = await redis.bitpos("users-subscribed", 1)
console.log(result) // 7
The bitop
command will allow you to run bit operations on bit maps. For example, you can use And, Or, XOR and NOT on two bitmap keys.
// Set Bit
await redis.setbit("users-subscribed-2", 6, 1)
// Bit Operation
const result = await redis.bitop("AND", "newkey", "users-subscribed", "users-subscribed-2")
console.log(result) // 7
const result2 = await redis.get("newkey")
console.log(result2) // 7
The bitfield
command allows you to perform multiple operations on a bitmap, which is helpful for perfoamnce boosting. The syntax is a bit confusing but first we pass the key to the command, then a series of commands to operation.
The series of commands must have the formmat (command, format, offset, value). For example, below we increment the 5bit signed integer offset 100 by 4 and then we get offset 2 in an unsigned interger format.
// Bit Field
const result = await redis.bitfield("bit-map", "incrby", "i5", 100, 4, "get", u4, 2)
console.log(result) // 7
The final command we will look at is bitcount
. This allows us to count all the number of set bits, value of 1, in a string. We can also bass a start and end value to count a range.
// Bit Count
const result = await redis.bitcount("users-subscribed")
console.log(result) // 7
const result2 = await redis.bitcount("users-subscribed", 0, 4)
console.log(result2) // 7