A Redis stream is created implicitly when the first message is added. XADD returns an automatically generated message ID when * is used; this is the usual approach, though an ID can also be supplied manually.
127.0.0.1:6379> XADD mystream * user david msg Hello
"1695864755027-0"
mystream is the stream name. Starting with Redis 6.2, NOMKSTREAM prevents automatic creation when the stream does not exist.
XREAD reads messages. A start value of 0 means the first message; a message ID can also be used as the starting point.
127.0.0.1:6379> XREAD streams mystream 0
1) 1) "mystream"
2) 1) 1) "1695864755027-0"
2) 1) "user"
2) "david"
3) "msg"
4) "Hello"
COUNT limits how many messages are returned:
127.0.0.1:6379> XREAD COUNT 1 streams mystream 0
1) 1) "mystream"
2) 1) 1) "1695864755027-0"
2) 1) "user"
2) "david"
3) "msg"
4) "Hello"
XREAD is non-blocking by default. Adding BLOCK switches it to blocking mode: it returns immediately if a message is available, otherwise waits. In blocking mode, $ represents the latest message ID; in non-blocking mode $ has no meaning.
127.0.0.1:6379> XREAD BLOCK 1000 streams mystream $
(nil)
(1.08s)
When several consumers read a stream with plain XREAD, each can see the same messages. A consumer group changes that: consumers in the group cooperate, so each message is delivered to one consumer within the group. This is useful when one consumer cannot handle the full load.
Test data:
127.0.0.1:6379> MULTI
127.0.0.1:6379> XADD mystream * msg 1
127.0.0.1:6379> XADD mystream * msg 2
127.0.0.1:6379> XADD mystream * msg 3
127.0.0.1:6379> XADD mystream * msg 4
127.0.0.1:6379> XADD mystream * msg 5
127.0.0.1:6379> EXEC
1) "1695865694880-0"
2) "1695865694880-1"
3) "1695865694880-2"
4) "1695865694880-3"
5) "1695865694880-4"
Plain XREAD does not put messages into a pending state. Pending state and acknowledgement apply to consumer groups. Messages read with XREAD remain in the stream, so multiple readers can still access them.
Create a group:
127.0.0.1:6379> XGROUP CREATE mystream mqGroup 0
OK
The 0 has the same meaning as in XREAD: start at the beginning of the stream. MKSTREAM creates the stream if it does not exist:
127.0.0.1:6379> XGROUP CREATE mystream2 mqGroup2 0 MKSTREAM
OK
XINFO GROUPS lists groups:
127.0.0.1:6379> XINFO GROUPS mystream
1) 1) "name"
2) "mqGroup"
3) "consumers"
4) (integer) 0
5) "pending"
6) (integer) 0
7) "last-delivered-id"
8) "0-0"
XINFO CONSUMERS returns an empty array before any group consumer has read:
127.0.0.1:6379> XINFO CONSUMERS mystream mqGroup
(empty array)
Read within a group:
127.0.0.1:6379> XREADGROUP group mqGroup consumerA count 1 streams mystream >
1) 1) "mystream"
2) 1) 1) "1695864755027-0"
2) 1) "user"
2) "david"
3) "msg"
4) "Hello"
127.0.0.1:6379> XREADGROUP group mqGroup consumerA count 1 streams mystream >
1) 1) "mystream"
2) 1) 1) "1695865694880-0"
2) 1) "msg"
2) "1"
127.0.0.1:6379> XREADGROUP group mqGroup consumerB count 1 streams mystream >
1) 1) "mystream"
2) 1) 1) "1695865694880-1"
2) 1) "msg"
2) "2"
Consumers are created automatically. > means messages not yet delivered to the group; count 1 returns one. The group records last-delivered-id, so it can continue after that point and avoid delivering the same message twice within the group.
If a consumer reads a message and then crashes, the message can be left unprocessed while other group consumers cannot receive it again. The pending list records delivered but unacknowledged messages.
XPENDING mystream mqGroup summarizes the pending list:
127.0.0.1:6379> XPENDING mystream mqGroup
1) (integer) 3
2) "1695864755027-0" # start message ID
3) "1695865694880-1" # end message ID
4) 1) 1) "consumerA"
2) "2"
2) 1) "consumerB"
2) "1"
This shows consumerA has two pending messages and consumerB has one.
Detailed pending entries:
127.0.0.1:6379> XPENDING mystream mqGroup - + 10
1) 1) "1695864755027-0"
2) "consumerA"
3) (integer) 588491
4) (integer) 1
2) 1) "1695865694880-0"
2) "consumerA"
3) (integer) 579214
4) (integer) 1
3) 1) "1695865694880-1"
2) "consumerB"
3) (integer) 237014
4) (integer) 1
The fields are message ID, consumer name, idle time in milliseconds, and delivery count. These messages have been read but not acknowledged.
XACK marks a message as processed:
127.0.0.1:6379> XACK mystream mqGroup 1695864755027-0
(integer) 1
After that, the pending detail no longer contains message ID 1695864755027-0:
127.0.0.1:6379> XPENDING mystream mqGroup - + 10
1) 1) "1695865694880-0"
2) "consumerA"
3) (integer) 728248
4) (integer) 1
2) 1) "1695865694880-1"
2) "consumerB"
3) (integer) 386048
4) (integer) 1
The pending list is what prevents loss: when a consumer returns it can retrieve its pending messages; if it cannot return, another consumer can take them over.
XCLAIM transfers a message. It needs the group, target consumer, message ID, and an IDLE threshold; only messages idle longer than the threshold are transferred.
127.0.0.1:6379> XCLAIM mystream mqGroup consumerB 360000 1695865694880-0
1) 1) "1695865694880-0"
2) 1) "msg"
2) "1"
This transfers message ID 1695865694880-0, whose idle time exceeds 360 seconds, into consumerB's pending list. The idle threshold prevents every pending entry from being moved too quickly.
127.0.0.1:6379> XPENDING mystream mqGroup - + 10
1) 1) "1695865694880-0"
2) "consumerB"
3) (integer) 11479
4) (integer) 2
2) 1) "1695865694880-1"
2) "consumerB"
3) (integer) 680393
4) (integer) 1
consumerA no longer has that message; consumerB does. The delivery count is now 2 and the idle time has been reset, which reduces repeated transfers. Running the same XCLAIM twice in a row will not succeed if the threshold is not met again:
127.0.0.1:6379> XCLAIM mystream mqGroup consumerB 360000 1695865694880-0
127.0.0.1:6379> XCLAIM mystream mqGroup consumerB 360000 1695865694880-0
XCLAIM can also be used by the original consumer when it comes back online to reacquire its pending messages. So it has two uses: reacquiring pending messages and transferring ownership.
A message that is delivered repeatedly but never acknowledged may be a dead letter. When the delivery count crosses a chosen threshold, treat it as bad and delete it. XRANGE lists all stream messages:
127.0.0.1:6379> XRANGE mystream - +
1) 1) "1695864755027-0"
2) 1) "user"
2) "david"
3) "msg"
4) "Hello"
2) 1) "1695865694880-0"
2) 1) "msg"
2) "1"
3) 1) "1695865694880-1"
2) 1) "msg"
2) "2"
4) 1) "1695865694880-2"
2) 1) "msg"
2) "3"
5) 1) "1695865694880-3"
2) 1) "msg"
2) "4"
6) 1) "1695865694880-4"
2) 1) "msg"
2) "5"
XDEL removes messages from the stream:
127.0.0.1:6379> XDEL mystream 1695864755027-0
(integer) 1
127.0.0.1:6379> XDEL mystream 1695865694880-0
(integer) 1
XRANGE after deletion:
127.0.0.1:6379> XRANGE mystream - +
1) 1) "1695865694880-1"
2) 1) "msg"
2) "2"
2) 1) "1695865694880-2"
2) 1) "msg"
2) "3"
3) 1) "1695865694880-3"
2) 1) "msg"
2) "4"
4) 1) "1695865694880-4"
2) 1) "msg"
2) "5"
The pending list can still contain a deleted message ID; use XACK to clear it:
127.0.0.1:6379> XPENDING mystream mqGroup - + 10
1) 1) "1695865694880-0" # <--- still exists
2) "consumerB"
3) (integer) 947553
4) (integer) 2
2) 1) "1695865694880-1"
2) "consumerB"
3) (integer) 1616467
4) (integer) 1
Check pending:
127.0.0.1:6379> XPENDING mystream mqGroup - + 10
1) 1) "1695865694880-0"
2) "consumerB"
3) (integer) 1048972
4) (integer) 2
2) 1) "1695865694880-1"
2) "consumerB"
3) (integer) 1717886
4) (integer) 1
127.0.0.1:6379> XACK mystream mqGroup 1695865694880-0
(integer) 1
127.0.0.1:6379> XPENDING mystream mqGroup - + 10
1) 1) "1695865694880-1"
2) "consumerB"
3) (integer) 1840638
4) (integer) 1
A deleted message can remain pending until it is acknowledged.
There is no dedicated command for creating an empty stream. Use XADD, or create a consumer group with MKSTREAM:
127.0.0.1:6379> xgroup create s g $ mkstream
OK
127.0.0.1:6379> xgroup destroy s g
(integer) 1
XINFO stream mystream reports stream details:
127.0.0.1:6379> XINFO stream mystream
1) "length"
2) (integer) 4
3) "radix-tree-keys"
4) (integer) 1
5) "radix-tree-nodes"
6) (integer) 2
7) "last-generated-id"
8) "1695865694880-4"
9) "groups"
10) (integer) 1
11) "first-entry"
12) 1) "1695865694880-1"
2) 1) "msg"
2) "2"
13) "last-entry"
14) 1) "1695865694880-4"
2) 1) "msg"
2) "5"
The output includes length, radix-tree keys and nodes, last-generated-id, group count, first-entry, and last-entry.
A stream has no automatic length cap. XTRIM can apply a limit at execution time:
127.0.0.1:6379> xadd s * msg 1
"1695871306010-0"
127.0.0.1:6379> xadd s * msg 2
"1695871307960-0"
127.0.0.1:6379> xadd s * msg 3
"1695871310232-0"
127.0.0.1:6379> xadd s * msg 4
"1695871311338-0"
127.0.0.1:6379> xadd s * msg 5
"1695871312698-0"
127.0.0.1:6379> xadd s * msg 6
"1695871314097-0"
127.0.0.1:6379> XTRIM s MAXLEN 5
(integer) 1
127.0.0.1:6379> XRANGE s - +
1) 1) "1695871307960-0"
2) 1) "msg"
2) "2"
2) 1) "1695871310232-0"
2) 1) "msg"
2) "3"
3) 1) "1695871311338-0"
2) 1) "msg"
2) "4"
4) 1) "1695871312698-0"
2) 1) "msg"
2) "5"
5) 1) "1695871314097-0"
2) 1) "msg"
2) "6"
MAXLEN 5 keeps the newest five entries at that moment. Later additions can make the stream grow again.
Starting with Redis 6.2, MINID removes entries with IDs lower than the given value:
127.0.0.1:6379> XTRIM s MINID 1695871312698
(integer) 3
127.0.0.1:6379> XRANGE s - +
1) 1) "1695871312698-0"
2) 1) "msg"
2) "5"
2) 1) "1695871314097-0"
2) 1) "msg"
2) "6"