SETBIT(3valkey)

πŸ“¦
version 9.0.6 Β· 2 versions
, in fedora 44

πŸ’– SETBIT β€” the valkey manual page, bestie edition πŸ’–

okay so you found the SETBIT(3valkey) page in the Valkey Command Manual and you're like "bits?? in my string??" β€” yes babe, and it's iconic. sit down, grab a strawberry πŸ“, we're doing this.


✨ NAME ✨

setbit β€” sets or clears the bit at offset of the string value. Creates the key if it doesn't exist. no cap. 🫢


✨ SYNOPSIS ✨

SETBIT key offset value

that's it. three little guys. main character energy fr fr. πŸ‘‘


✨ DESCRIPTION ✨

So like, SETBIT sets or clears the bit at offset in the string value that's chillin' at key. πŸ’…

Whether it gets set or cleared depends on value, which is literally only allowed to be 0 or 1. pick one bestie.

When key doesn't exist? boom, a whole new string value gets created. ✨ The string gets grown so it can definitely hold a bit at offset. The offset argument has to be greater than or equal to 0, and smaller than 2^32 β€” which is the thing that limits bitmaps to 512MB, iykyk. And when the string at key grows, all the added bits are set to 0. free real estate.

⚠️ Warning, and I need you to actually read this one: when you're setting the last possible bit (offset equal to 2^32 -1) and the string value stored at key doesn't yet hold a string value, or holds a small string value, Valkey needs to allocate all the intermediate memory, and that can block the server for some time. 😭 On a 2010 MacBook Pro, setting bit number 2^32 -1 (512MB allocation) takes ~300ms, setting bit number 2^30 -1 (128MB allocation) takes ~80ms, setting bit number 2^28 -1 (32MB allocation) takes ~30ms and setting bit number 2^26 -1 (8MB allocation) takes ~8ms. But bestie β€” once that first allocation is done, subsequent calls to SETBIT for the same key won't have the allocation overhead anymore. slay. πŸ’…

✨ REPLY ✨

valkey-protocol(7) Integer reply: the original bit value stored at offset. πŸŽ€


✨ COMPLEXITY ✨

O(1)

constant time, she's efficient, we love her. ⭐


✨ ACL CATEGORIES ✨

  • @bitmap
  • @slow
  • @write

✨ HISTORY ✨

  • Available since: 2.2.0 β€” a true veteran, been serving looks since forever. πŸ‘‘

✨ EXAMPLES ✨

127.0.0.1:6379> SETBIT mykey 7 1
(integer) 0
127.0.0.1:6379> SETBIT mykey 7 0
(integer) 1
127.0.0.1:6379> GET mykey
"\x00"

see how the reply is the original bit value? the first call said 0 (it was off before), the second said 1 (it was on before). it's giving receipts. πŸ§ΎπŸ’–


🌸 Pattern: accessing the entire bitmap 🌸

Sometimes you need to set all the bits of a single bitmap at once β€” like when you're initializing it to a default non-zero value, no big deal. You could do it with multiple calls to the SETBIT command, one for each bit that needs setting. but as an optimization?? πŸ‘€ you can use a single valkey-set(7) SET command to set the entire bitmap. ate.

Bitmaps are not an actual data type, bestie β€” they're a set of bit-oriented operations defined on the String type (for more info, refer to the Bitmaps section of the Data Types Introduction page). Which means bitmaps can be used with string commands, and most importantly with valkey-set(7) SET and valkey-get(7) GET. 🌈

Because Valkey's strings are binary-safe, a bitmap is trivially encoded as a bytes stream. The first byte of the string corresponds to offsets 0..7 of the bitmap, the second byte to the 8..15 range, and so forth. math is beautiful actually.

For example, after setting a few bits, getting the string value of the bitmap would look like this:

> SETBIT bitmapsarestrings 2 1
> SETBIT bitmapsarestrings 3 1
> SETBIT bitmapsarestrings 5 1
> SETBIT bitmapsarestrings 10 1
> SETBIT bitmapsarestrings 11 1
> SETBIT bitmapsarestrings 14 1
> GET bitmapsarestrings
"42"

By getting the string representation of a bitmap, the client can then parse the response's bytes by extracting the bit values using native bit operations in its native programming language. Symmetrically, it's also possible to set an entire bitmap by performing the bits-to-bytes encoding in the client and calling SET with the resultant string. teamwork, baby. 🫢


πŸ¦‹ Pattern: setting multiple bits πŸ¦‹

SETBIT excels at setting single bits, and can be called several times when multiple bits need to be set. But to optimize this operation you can replace multiple SETBIT calls with a single call to the variadic valkey-bitfield(7) BITFIELD command and the use of fields of type u1. genius behavior honestly. ✨

For example, the example above could be replaced by:

> BITFIELD bitsinabitmap SET u1 2 1 SET u1 3 1 SET u1 5 1 SET u1 10 1 SET u1 11 1 SET u1 14 1

πŸ‘‘ Advanced Pattern: accessing bitmap ranges πŸ‘‘

It's also possible to use the valkey-getrange(7) GETRANGE and valkey-setrange(7) SETRANGE string commands to efficiently access a range of bit offsets in a bitmap. Below is a sample implementation in idiomatic Valkey Lua scripting that can be run with the valkey-eval(7) EVAL command:

--[[
Sets a bitmap range

Bitmaps are stored as Strings in Valkey. A range spans one or more bytes,
so we can call `SETRANGE` when entire bytes need to be set instead of flipping
individual bits. Also, to avoid multiple internal memory allocations in
Valkey, we traverse in reverse.
Expected input:
  KEYS[1] - bitfield key
  ARGV[1] - start offset (0-based, inclusive)
  ARGV[2] - end offset (same, should be bigger than start, no error checking)
  ARGV[3] - value (should be 0 or 1, no error checking)
]]--

-- A helper function to stringify a binary string to semi-binary format
local function tobits(str)
  local r = ''
  for i = 1, string.len(str) do
    local c = string.byte(str, i)
    local b = ' '
    for j = 0, 7 do
      b = tostring(bit.band(c, 1)) .. b
      c = bit.rshift(c, 1)
    end
    r = r .. b
  end
  return r
end

-- Main
local k = KEYS[1]
local s, e, v = tonumber(ARGV[1]), tonumber(ARGV[2]), tonumber(ARGV[3])

-- First treat the dangling bits in the last byte
local ms, me = s % 8, (e + 1) % 8
if me > 0 then
  local t = math.max(e - me + 1, s)
  for i = e, t, -1 do
    server.call('SETBIT', k, i, v)
  end
  e = t
end

-- Then the danglings in the first byte
if ms > 0 then
  local t = math.min(s - ms + 7, e)
  for i = s, t, 1 do
    server.call('SETBIT', k, i, v)
  end
  s = t + 1
end

-- Set a range accordingly, if at all
local rs, re = s / 8, (e + 1) / 8
local rl = re - rs
if rl > 0 then
  local b = '\255'
  if 0 == v then
    b = '\0'
  end
  server.call('SETRANGE', k, rs, string.rep(b, rl))
end
πŸ“ Note: the implementation for getting a range of bit offsets from a bitmap is left as an exercise to the reader. bestie you're on your own for that one, but I believe in you πŸ’œ

✨ SEE ALSO ✨


and that's the whole page, periodt. πŸ’… this was SETBIT, version 9.0.0, dated 2025-10-21 β€” go forth and flip some bits, bestie, you're literally gonna slay. πŸŽ€βœ¨πŸ’–πŸ¦‹πŸŒΈ

πŸ“¦ where this page comes from