SETBIT
SETBIT key offset value
- 可用版本
- Redis 开源版 2.2.0
- 时间复杂度
- O(1)
- ACL 类别
-
@write
,@bitmap
,@slow
,
设置或清除存储在 *key* 的字符串值中 *offset* 处的位。
根据 *value* 的值(可以是 0 或 1)来设置或清除该位。
当 *key* 不存在时,将创建一个新的字符串值。字符串会被扩展,以确保它可以在 *offset* 处容纳一个位。*offset* 参数必须大于或等于 0,且小于 2^32 (这限制了位图的大小不超过 512MB)。当 *key* 处的字符串被扩展时,新增的位将设置为 0。
警告:当设置最后一个可能的位(*offset* 等于 2^32 -1)且存储在 *key* 处的字符串值尚未持有字符串值或只持有较小的字符串值时,Redis 需要分配所有中间内存,这可能会阻塞服务器一段时间。在 2010 年的 MacBook Pro 上,设置位号 2^32 -1 (512MB 分配) 大约需要 300 毫秒,设置位号 2^30 -1 (128MB 分配) 大约需要 80 毫秒,设置位号 2^28 -1 (32MB 分配) 大约需要 30 毫秒,设置位号 2^26 -1 (8MB 分配) 大约需要 8 毫秒。请注意,一旦完成首次分配,后续对同一 *key* 的 SETBIT
调用将不会有分配开销。
示例
模式:访问整个位图
有时需要一次性设置单个位图的所有位,例如将其初始化为非零默认值。虽然可以使用多个 SETBIT
命令调用(每个需要设置的位调用一次)来实现,但作为一种优化,您可以使用单个 SET
命令来设置整个位图。
位图并非实际的数据类型,而是在 String 类型上定义的一组面向位的操作(更多信息请参阅数据类型介绍页面的位图部分)。这意味着位图可以与字符串命令一起使用,最重要的是与 SET
和 GET
命令一起使用。
由于 Redis 的字符串是二进制安全的,位图可以很容易地编码为字节流。字符串的第一个字节对应于位图的偏移量 0..7,第二个字节对应于偏移量 8..15,依此类推。
例如,设置一些位后,获取位图的字符串值会是这样
> 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"
通过获取位图的字符串表示,客户端可以在其原生编程语言中使用原生的位操作来提取位值,从而解析响应的字节。类似地,也可以在客户端执行位到字节的编码,然后调用 SET
命令并传入结果字符串来设置整个位图。
模式:设置多个位
SETBIT
擅长设置单个位,当需要设置多个位时可以调用多次。为了优化此操作,您可以使用可变参数的 BITFIELD
命令,配合使用类型为 u1
的字段,来替换多个 SETBIT
调用。
例如,上面的示例可以替换为
> 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
高级模式:访问位图范围
还可以使用 GETRANGE
和 SETRANGE
字符串命令来高效地访问位图中的一系列位偏移量。下面是一个典型的 Redis Lua 脚本实现示例,可以使用 EVAL
命令运行。
--[[
Sets a bitmap range
Bitmaps are stored as Strings in Redis. A range spans one or more bytes,
so we can call [`SETRANGE`](/docs/latest/commands/setrange/) 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
注意:从位图中获取一系列位偏移量的实现留作读者的练习。