Redis Setbit 命令用于对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。
根据值 value 是 1 或 0 来决定设置或清除位 bit。当 key 不存在时会创建一个新的字符串。
当字符串不够长时,字符串的长度将增大,以确保它可以在offset位置存储值。
offset 参数需要大于等于0,并且小于 232 (bitmaps 最大 512MB)。
当值字符串变长时,添加的 bit 会被设置为 0。
注意: 当设置的是最后一位 bit (offset 等于 232 -1),并且存储在 key 中的字符串还没有存储一个字符串值,或者存储的是一个短的字符串值时,Redis 需要分配所有的中间内存,这会阻塞 Redis 服务器一段时间。
在 2010 年的 MacBook Pro上,设置 232 -1 位 (分配512MB内存) 需花费 300ms,设置 230 -1 位 (分配 128MB 内存) 需花费 80m,设置 228 -1 位 (分配 32MB 内存) 需花费 30ms,设置 226 -1 位 (分配 8MB 内存) 需花费 8ms。
需要注意的是,一旦上面第一步内存分配被完成,对于同一个 key 接下来调用 SETBIT 将不会有分配内存开销。
*返回值
整数: 存储在 offset 偏移位的原始值。
*例子
(integer) 0redis> SETBIT mykey 7 0
(integer) 1redis> GET mykey
"\u0000"
*Pattern: accessing the entire bitmap
There are cases when you need to set all the bits of single bitmap at once, for example when initializing it to a default non-zero value. It is possible to do this with multiple calls to the SETBIT command, one for each bit that needs to be set. However, so as an optimization you can use a single SET command to set the entire bitmap.
Bitmaps are not an actual data type, but a set of bit-oriented operations defined on the String type (for more information refer to the Bitmaps section of the Data Types Introduction page). This means that bitmaps can be used with string commands, and most importantly with SET and GET.
Because Redis' 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.
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 is also possible to set an entire bitmap by performing the bits-to-bytes encoding in the client and calling SET with the resultant string.
*Pattern: setting multiple bits
SETBIT excels at setting single bits, and can be called several times when
multiple bits need to be set. To optimize this operation you can replace
multiple SETBIT calls with a single call to the variadic BITFIELD command
and the use of fields of type u1
.
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 is also possible to use the GETRANGE and SETRANGE string commands to efficiently access a range of bit offsets in a bitmap. Below is a sample implementation in idiomatic Redis Lua scripting that can be run with the EVAL command:
--[[
Sets a bitmap range
Bitmaps are stored as Strings in Redis. A range spans one or more bytes,
so we can call [SETRANGE](/commands/setrange.html) when entire bytes need to be set instead of flipping
individual bits. Also, to avoid multiple internal memory allocations in
Redis, 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
redis.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
redis.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
redis.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.