在计算机中,全部的数据在存储和运算时都要使用二进制数表示(由于计算机用高电平和低电平分别表示1和0),例如,象a、b、c、d这样的 52 个字母(包括大写)、以及 0、1 等数字还有一些经常使用的符号(例如*、#、@等)在计算机中存储时也要使用二进制数来表示,而具体用哪些二进制数字表示哪一个符号,固然每一个人均可以约定本身的一套(这就叫编码),而你们若是要想互相通讯而不形成混乱,那么你们就必须使用相同的编码规则,因而美国有关的标准化组织就出台了所谓的 ASCII 编码,统一规定了上述经常使用符号用哪些二进制数来表示。例如 "Redis" 字符串是由 5 个字节组成,计算机存储时使用其二进制表示,首先找到每一个字母中的 ASCII 码,而后对应到二进制。redis
许多开发语言都提供了操做位的功能,合理地使用 位 可以有效地提升内存使用率和开发效率。Redis提供了Bitmaps这个 “数据结构” 能够实现对位的操做。把数据结构加上引号主要由于:shell
Bitmaps 自己不是一种数据结构,实际上它就是字符串(key 对应的 value 就是上图中最后的一串二进制),可是它能够对字符串的位进行操做。数组
Bitmaps 单独提供了一套命令,因此在 Redis 中使用 Bitmaps 和使用字符串的方法不太相同。能够把 Bitmaps 想象成一个以 位 为单位的数组,数组的每一个单元只能存储 0 和 1,数组的下标在Bitmaps中叫作偏移量。服务器
自2.2.0可用。数据结构
时间复杂度:O(1)。性能
对 key
所储存的字符串值,设置或清除指定偏移量上的位(bit)。大数据
位的设置或清除取决于 value
参数,能够是 0
也能够是 1
。编码
当 key
不存在时,自动生成一个新的字符串值。spa
字符串会进行伸展(grown)以确保它能够将 value
保存在指定的偏移量上。当字符串值进行伸展时,空白位置以 0
填充。code
offset
参数必须大于或等于 0
,小于 2^32 (bit 映射被限制在 512 MB 以内)。
对使用大的 offset
的 SETBIT
操做来讲,内存分配可能形成 Redis 服务器被阻塞。
字符串值指定偏移量上原来储存的位(bit)。
# SETBIT 会返回以前位的值(默认是 0)这里会生成 619 个位【以前为空因此扩展到 618 时须要 619 个位(从 0 开始)】 coderknock> SETBIT testBit 618 1 (integer) 0 coderknock> SETBIT testBit 618 0 (integer) 1 coderknock> SETBIT testBit 618 1 (integer) 0 coderknock> GETBIT testBit 618 (integer) 1 coderknock> GETBIT testBit 100 (integer) 0 # SETBIT value 只能是 0 或者 1 coderknock> SETBIT testBit 618 2 (error) ERR bit is not an integer or out of range
自2.2.0可用。
时间复杂度:O(1)。
对 key
所储存的字符串值,获取指定偏移量上的位(bit)。
当 offset
比字符串值的长度大,或者 key
不存在时,返回 0
。
字符串值指定偏移量上的位(bit)。
coderknock> EXISTS bit (integer) 0 coderknock> SETBIT bit 618 1 (integer) 0 coderknock> GETBIT bit 618 (integer) 1 # 不存在的偏移量也会取到 0 coderknock> GETBIT bit 619 (integer) 0 # 能够看到 bit 自己也是个字符串 coderknock> GET bit "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 "
自2.6.0可用。
时间复杂度:O(N)。
计算给定字符串中,被设置为 1
的比特位的数量。
通常状况下,给定的整个字符串都会被进行计数,经过指定额外的 start
或 end
参数,可让计数只在特定的位上进行。
start
和 end
参数的设置和 GETRANGE
命令相似,均可以使用负数值: 好比 -1
表示最后一个字节, -2
表示倒数第二个字节,以此类推。
不存在的 key
被当成是空字符串来处理,所以对一个不存在的 key
进行 BITCOUNT
操做,结果为 0
。
被设置为 1
的位的数量。
# 此处的 bit 基于 GETBIT 示例的命令中的 coderknock> BITCOUNT bit (integer) 1 # 计算 bit 中全部值为 1 的位的个数 coderknock> BITCOUNT bit (integer) 1 coderknock> SETBIT bit 0 1 (integer) 0 coderknock> BITCOUNT bit (integer) 2 # 计算指定位置 bit 中全部值为 1 的位的个数 coderknock> BITCOUNT bit 10 619 (integer) 1
自2.6.0可用。
时间复杂度:O(N)。
对一个或多个保存二进制位的字符串 key
进行位元操做,并将结果保存到 destkey
上。
operation
能够是 AND
、 OR
、 NOT
、 XOR
这四种操做中的任意一种:
BITOP AND destkey key [key ...]
,对一个或多个 key
求逻辑并,并将结果保存到 destkey
。
BITOP OR destkey key [key ...]
,对一个或多个 key
求逻辑或,并将结果保存到 destkey
。
BITOP XOR destkey key [key ...]
,对一个或多个 key
求逻辑异或,并将结果保存到 destkey
。
BITOP NOT destkey key
,对给定 key
求逻辑非,并将结果保存到 destkey
。
除了 NOT
操做以外,其余操做均可以接受一个或多个 key
做为输入。
处理不一样长度的字符串
当 BITOP
处理不一样长度的字符串时,较短的那个字符串所缺乏的部分会被看做 0
。
空的 key
也被看做是包含 0
的字符串序列。
保存到 destkey
的字符串的长度,和输入 key
中最长的字符串长度相等。
coderknock> SETBIT bits-1 0 1 (integer) 0 coderknock> SETBIT bits-1 3 1 (integer) 0 # bits-1 为 1001 coderknock> SETBIT bits-2 0 1 (integer) 0 coderknock> SETBIT bits-2 1 1 (integer) 0 coderknock> SETBIT bits-2 3 1 (integer) 0 # bits-2 为 1011 #bits-1 bits-2 作 并 操做 coderknock> BITOP AND and-result bits-1 bits-2 (integer) 1 coderknock> GETBIT and-result 0 (integer) 1 coderknock> GETBIT and-result 1 (integer) 0 coderknock> GETBIT and-result 2 (integer) 0 coderknock> GETBIT and-result 3 (integer) 1 #and-result 1001 #bits-1 bits-2 作 或 操做 coderknock> BITOP OR or-result bits-1 bits-2 (integer) 1 coderknock> GETBIT and-result 0 (integer) 1 coderknock> GETBIT and-result 1 (integer) 0 coderknock> GETBIT and-result 2 (integer) 0 coderknock> GETBIT and-result 3 (integer) 1 #or-result 1011 # 非 操做只能针对一个 key coderknock> BITOP NOT not-result bits-1 bits-2 (error) ERR BITOP NOT must be called with a single source key. coderknock> BITOP NOT not-result bits-1 (integer) 1 coderknock> GETBIT not-result 0 (integer) 0 coderknock> GETBIT not-result 1 (integer) 1 coderknock> GETBIT not-result 2 (integer) 1 coderknock> GETBIT not-result 3 (integer) 0 # not-result 0110 # 异或操做 coderknock> BITOP XOR xor-result bits-1 bits-2 (integer) 1 coderknock> GETBIT xor-result 0 (integer) 0 coderknock> GETBIT xor-result 1 (integer) 1 coderknock> GETBIT xor-result 2 (integer) 0 coderknock> GETBIT xor-result 3 (integer) 0 # xor-result 0010
BITOP
的复杂度为 O(N) ,当处理大型矩阵(matrix)或者进行大数据量的统计时,最好将任务指派到附属节点(slave)进行,避免阻塞主节点。
自2.8.7可用。
时间复杂度:O(N)。
返回字符串里面第一个被设置为 1 或者 0 的bit位。
返回一个位置,把字符串当作一个从左到右的字节数组,第一个符合条件的在位置 0,其次在位置 8,等等。
GETBIT
和 SETBIT
类似的也是操做字节位的命令。
默认状况下整个字符串都会被检索一次,只有在指定 start 和 end 参数(指定start和end位是可行的),该范围被解释为一个字节的范围,而不是一系列的位。因此start=0
而且 end=2
是指前三个字节范围内查找。
注意,返回的位的位置始终是从 0 开始的,即便使用了 start 来指定了一个开始字节也是这样。
和 GETRANGE
命令同样,start 和 end 也能够包含负值,负值将从字符串的末尾开始计算,-1是字符串的最后一个字节,-2是倒数第二个,等等。
不存在的key将会被当作空字符串来处理。
命令返回字符串里面第一个被设置为 1 或者 0 的 bit 位。
若是咱们在空字符串或者 0 字节的字符串里面查找 bit 为1的内容,那么结果将返回-1。
若是咱们在字符串里面查找 bit 为 0 并且字符串只包含1的值时,将返回字符串最右边的第一个空位。若是有一个字符串是三个字节的值为 0xff
的字符串,那么命令 BITPOS key 0
将会返回 24,由于 0-23 位都是1。
基本上,咱们能够把字符串当作右边有无数个 0。
然而,若是你用指定 start 和 end 范围进行查找指定值时,若是该范围内没有对应值,结果将返回 -1。
redis> SET mykey "\xff\xf0\x00" OK redis> BITPOS mykey 0 # 查找字符串里面bit值为0的位置 (integer) 12 redis> SET mykey "\x00\xff\xf0" OK redis> BITPOS mykey 1 0 # 查找字符串里面bit值为1从第0个字节开始的位置 (integer) 8 redis> BITPOS mykey 1 2 # 查找字符串里面bit值为1从第2个字节(12)开始的位置 (integer) 16 redis> set mykey "\x00\x00\x00" OK redis> BITPOS mykey 1 # 查找字符串里面bit值为1的位置 (integer) -1
自3.2.0可用。
时间复杂度:每一个子命令的复杂度为 O(1) 。
BITFIELD key GET type offset INCRBY type offset increment
`BITFIELD` 命令能够将一个 Redis 字符串看做是一个由二进制位组成的数组, 并对这个数组中储存的长度不一样的整数进行访问 (被储存的整数无需进行对齐)。 换句话说, 经过这个命令, 用户能够执行诸如 “对偏移量 1234 上的 5 位长有符号整数进行设置”、 “获取偏移量 4567 上的 31 位长无符号整数”等操做。 此外, `BITFIELD` 命令还能够对指定的整数执行加法操做和减法操做, 而且这些操做能够经过设置妥善地处理计算时出现的溢出状况。
BITFIELD
命令能够在一次调用中同时对多个位范围进行操做: 它接受一系列待执行的操做做为参数, 并返回一个数组做为回复, 数组中的每一个元素就是对应操做的执行结果。
好比如下命令就展现了如何对位于偏移量 100 的 8 位长有符号整数执行加法操做, 并获取位于偏移量 0 上的 4 位长无符号整数:
coderknock> BITFIELD mykey INCRBY i8 100 1 GET u4 0 1) (integer) 1 2) (integer) 0
注意:
使用 GET
子命令对超出字符串当前范围的二进制位进行访问(包括键不存在的状况), 超出部分的二进制位的值将被当作是 0 。
使用 SET
子命令或者 INCRBY
子命令对超出字符串当前范围的二进制位进行访问将致使字符串被扩大, 被扩大的部分会使用值为 0 的二进制位进行填充。 在对字符串进行扩展时, 命令会根据字符串目前已有的最远端二进制位, 计算出执行操做所需的最小长度。
如下是 BITFIELD
命令支持的子命令:
GET <type> <offset>
—— 返回指定的二进制位范围。
SET <type> <offset> <value>
—— 对指定的二进制位范围进行设置,并返回它的旧值。
INCRBY <type> <offset> <increment>
—— 对指定的二进制位范围执行加法操做,并返回它的旧值。用户能够经过向 increment
参数传入负值来实现相应的减法操做。
除了以上三个子命令以外, 还有一个子命令, 它能够改变以后执行的 INCRBY
子命令在发生溢出状况时的行为:
OVERFLOW [WRAP|SAT|FAIL]
当被设置的二进制位范围值为整数时, 用户能够在类型参数的前面添加 i
来表示有符号整数, 或者使用 u
来表示无符号整数。 好比说, 咱们可使用 u8
来表示 8 位长的无符号整数, 也可使用 i16
来表示 16 位长的有符号整数。
BITFIELD
命令最大支持 64 位长的有符号整数以及 63 位长的无符号整数, 其中无符号整数的 63 位长度限制是因为 Redis 协议目前还没法返回 64 位长的无符号整数而致使的。
在二进制位范围命令中, 用户有两种方法来设置偏移量:
若是用户给定的是一个没有任何前缀的数字, 那么这个数字指示的就是字符串以零为开始(zero-base)的偏移量。
另外一方面, 若是用户给定的是一个带有 #
前缀的偏移量, 那么命令将使用这个偏移量与被设置的数字类型的位长度相乘, 从而计算出真正的偏移量。
好比说, 对于如下这个命令来讲:
BITFIELD mystring SET i8 #0 100 i8 #1 200
命令会把 mystring
键里面, 第一个 i8
长度的二进制位的值设置为 100
, 并把第二个 i8
长度的二进制位的值设置为 200
。 当咱们把一个字符串键当成数组来使用, 而且数组中储存的都是同等长度的整数时, 使用 #
前缀可让咱们免去手动计算被设置二进制位所在位置的麻烦。
用户能够经过 OVERFLOW
命令以及如下展现的三个参数, 指定 BITFIELD
命令在执行自增或者自减操做时, 碰上向上溢出(overflow)或者向下溢出(underflow)状况时的行为:
WRAP
: 使用回绕(wrap around)方法处理有符号整数和无符号整数的溢出状况。 对于无符号整数来讲, 回绕就像使用数值自己与可以被储存的最大无符号整数执行取模计算, 这也是 C 语言的标准行为。 对于有符号整数来讲, 上溢将致使数字从新从最小的负数开始计算, 而下溢将致使数字从新从最大的正数开始计算。 好比说, 若是咱们对一个值为 127
的 i8
整数执行加一操做, 那么将获得结果 -128
。
SAT
: 使用饱和计算(saturation arithmetic)方法处理溢出, 也便是说, 下溢计算的结果为最小的整数值, 而上溢计算的结果为最大的整数值。 举个例子, 若是咱们对一个值为 120
的 i8
整数执行加 10
计算, 那么命令的结果将为 i8
类型所能储存的最大整数值 127
。 与此相反, 若是一个针对 i8
值的计算形成了下溢, 那么这个 i8
值将被设置为 -127
。
FAIL
: 在这一模式下, 命令将拒绝执行那些会致使上溢或者下溢状况出现的计算, 并向用户返回空值表示计算未被执行。
须要注意的是, OVERFLOW
子命令只会对紧随着它以后被执行的 INCRBY
命令产生效果, 这一效果将一直持续到与它一同被执行的下一个 OVERFLOW
命令为止。 在默认状况下, INCRBY
命令使用 WRAP
方式来处理溢出计算。
如下是一个使用 OVERFLOW
子命令来控制溢出行为的例子:
coderknock> BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1 1) (integer) 1 2) (integer) 1 coderknock> BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1 1) (integer) 2 2) (integer) 2 coderknock> BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1 1) (integer) 3 2) (integer) 3 coderknock> BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1 1) (integer) 0 -- 使用默认的 WRAP 方式处理溢出 2) (integer) 3 -- 使用 SAT 方式处理溢出
而如下则是一个由于 OVERFLOW FAIL
行为而致使子命令返回空值的例子:
coderknock> BITFIELD mykey OVERFLOW FAIL incrby u2 102 1 1) (nil)
BITFIELD
命令的做用在于它可以将不少小的整数储存到一个长度较大的位图中, 又或者将一个很是庞大的键分割为多个较小的键来进行储存, 从而很是高效地使用内存, 使得 Redis 可以获得更多不一样的应用 —— 特别是在实时分析领域: BITFIELD
可以以指定的方式对计算溢出进行控制的能力, 使得它能够被应用于这一领域。
BITFIELD
在通常状况下都是一个快速的命令, 须要注意的是, 访问一个长度较短的字符串的远端二进制位将引起一次内存分配操做, 这一操做花费的时间可能会比命令访问已有的字符串花费的时间要长。
BITFIELD
把位图第一个字节偏移量 0 上的二进制位看做是 most significant 位, 以此类推。 举个例子, 若是咱们对一个已经预先被所有设置为 0 的位图进行设置, 将它在偏移量 7 的值设置为 5 位无符号整数值 23 (二进制位为 10111
), 那么命令将生产出如下这个位图表示:
+--------+--------+ |00000001|01110000| +--------+--------+
当偏移量和整数长度与字节边界进行对齐时, BITFIELD
表示二进制位的方式跟大端表示法(big endian)一致, 可是在没有对齐的状况下, 理解这些二进制位是如何进行排列也是很是重要的。
若是 member
元素是集合的成员,返回 1
。
若是 member
元素不是集合的成员,或 key
不存在,返回 0
。
coderknock> SISMEMBER saddTest add1 (integer) 1 # add7 元素不存在 coderknock> SISMEMBER saddTest add7 (integer) 0 # key 不存在 coderknock> SISMEMBER nonSet a (integer) 0 # key 类型不是集合 coderknock> SISMEMBER embstrKey a (error) WRONGTYPE Operation against a key holding the wrong kind of value
使用 bitmap 实现用户上线次数统计