セットを使用してその日のアクティブ ユーザーを記録する場合、ユーザー数が非常に多い場合、多くのスペースが無駄になります。したがって、redis はユーザーが各ビットを個別に操作できるようにビットマップを提供します。次の記事では、redis のビットマップについて説明します。お役に立てば幸いです。
ビットマップは、多数のビットで構成されるデータ構造です (各ビットは 0 と 1 のみです)。 、主にスペースを節約し、一部のシナリオでデータを有意義に記録するのに適しています。
たとえば、多数の bool 型アクセス、ユーザーの 365 日のチェックイン記録、チェックイン数が 1、サインインがない場合、値は 0 になります。通常のキー/値を保存に使用すると、ユーザー数が多い場合、大量の保存スペースが必要になります。
ストレージにビットマップを使用する場合、365 ビットを使用して 1 年 365 日を保存できます。365 ビットは 46 バイト (少し長い文字列) に変換されるため、時間を大幅に節約できます。ストレージ スペース、
#ビットマップの本質は、実際には通常の文字列、つまりバイト配列です。get/set を使用すると、ビットマップ全体の内容を直接取得および設定できます。 getbit/setbit を使用して、バイト配列をビット配列として扱うこともできます。 [関連する推奨事項: Redis ビデオ チュートリアル ]
通常、文字列を設定するには set コマンドを使用します。以下では setbit を使用します。ビット配列を設定し、最後にそれを文字列の形式で取得します。
最初に、次のように 2 つの ASCII コード h と e のバイナリ表現を取得します。
# h のバイナリ コードは 01101000、e のバイナリ コードは 01100101 であることがわかります。ビットが 1 になる位置に注目してビットを設定するだけです。 #必須 ビット配列の順序と文字のビット順序が逆になっていることに注意してください。この原則に従って、h 文字の各 1 の位置は 1/2 であると計算されます。 /4、e 文字の各 1 の位置は 9/10/13/15 です。, したがって、setbit を使用してビット配列を設定し、各位置の対応するビット配列 (1/2/4/9/10/13/15) 1.setbit data 1 1 setbit data 2 1 setbit data 4 1 setbit data 9 1 setbit data 10 1 setbit data 13 1 setbit data 15 1
ゼロデポジットと四捨五入の合計
最終的にデータ キーを直接取得すると、それが適切であることがわかります。
setbit の組み合わせを取得します。 get はゼロデポジットと整数取得と呼ばれます。ゼロデポジットはビットごとの設定です。取得はキー名から直接すべてを取得することです。データ、同様に、ゼロストレージとゼロ検索も実行できます。全体ストレージとゼロ取得。全体ストレージは文字列を直接使用してビット配列全体を設定し、ゼロ取得はビットの位置からビットを取得します。 #ゼロ デポジットとゼロ テイク#ご覧のとおり、setbit に従って w というキーを使用してビット配列のビットを設定します。 , そして設定のみ 1/2/4 の 3 つの位置の値は 1 です。下の図には、3 番目の位置の値を取得する getbit w 3 があります。このときのデフォルトは 0 です。ビジネスの観点から見ると、合計 4 日間のチェックインとして理解できます。3 日目にはサインインがありませんでした。# # 全額入金、少額出金
#午後に示したように、wのキーに対してh文字を直接設定し、そのビット配列の各ビットを設定します。 wはgetbitで取得します。取得した内容は上記のh文字のバイナリ内容と同じであることがわかります。1/2/4の位置が1、残りが0
#注意
redis のビット配列は自動拡張となっており、設定したオフセット位置が既存のコンテンツ範囲を超える場合、ビット配列は自動的に 0 ずつ拡張されます。つまり、拡張されたビットの値はデフォルトで 0 になります。対応するビットのバイトが印刷不可能な文字の場合、redis-cli はその文字の 16 進形式を表示します。
bitcount は指定された位置範囲内の 1 の数をカウントするために使用され、bitpos は指定された範囲内に現れる最初の 0 または 1 を見つけるために使用されます。
我们可以通过bitcount统计用户一共签到了多少天,通过bitpos指令查找用户从哪一天开始第一次签到,
如果指定了范围参数[start, end],就可以统计在某个时间范围内用户签到了多少天以及用户自某天以后的哪天开始签到,
但是需要注意的是,start和end参数是字节索引,也就是说,指定的位范围必须是8的倍数,
而不能任意指定,所以我们无法直接计算某个月内用户签到了多少天,如果需要计算的话,
可以使用getrange命令取出该月覆盖的字节内容,然后在内存中进行统计,例如2月覆盖了10-12个字节,就使用 getrange w 8 12 。
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitcount w # 所有字符中有多少个1 (integer) 21 127.0.0.1:6379> bitcount w 0 0 # 第一个字符中 1 的位数 (integer) 3 127.0.0.1:6379> bitcount w 0 1 # 前两个字符中 1 的位数 (integer) 7 127.0.0.1:6379> bitpos w 0 # 第一个 0 位 (integer) 0 127.0.0.1:6379> bitpos w 1 # 第一个 1 位 (integer) 1 127.0.0.1:6379> bitpos w 1 1 1 # 从第二个字符算起,第一个1位 (integer) 9 127.0.0.1:6379> bitpos w 1 2 2 # 从第三个字符算起,第一个1位 (integer) 17
之前介绍的 setbit / getbit 指定位的值都是单个位,如果要一次操作多个位,就必须使用管道来处理,
在redis3.2以后,提供了bitfield指令,可以一次对多个位进行操作,bitfield有三个子指令,分别是get/set/incrby, 都可以对指定位片段进行读写,
但是最多只能处理64个连续的位,如果超过64位,就需要使用多个子指令,bitfield可以一次执行多个子指令。
示例
下面对下图的位数组使用 bitfield 做一些操作
127.0.0.1:6379> bitfield w get u4 0 # 从第1个位开始取4个位,结果是无符号数(u) 1) (integer) 6 127.0.0.1:6379> bitfield w get u3 2 # 从第3个位开始取3个位,结果是无符号数(u) 1) (integer) 5 127.0.0.1:6379> bitfield w get i4 0 # 从第1个位开始取4个位,结果是有符号数(i) 1) (integer) 6 127.0.0.1:6379> bitfield w get i3 2 # 从第3个位开始取3个位,结果是有符号数(i) 1) (integer) -3
有符号数是指获取的位数组中的第一个位是符号位,剩下的才是值,如果第一位是1,就是负数,
无符号数表示非负数,没有符号位,获取的位数组全部都是值,有符号数最多可以获取64位,
无符号数只能获取63位,因为redis协议中的integer是有符号数,最大64位,不能传递64位的无符号值,
如果超出位数限制,redis就会告诉你参数错误。
上面的指令可以合并成一条指令,可以看到得到的结果是一样的,
bitfield w get u4 0 get u3 2 get i4 0 get i3 2
set修改
我们从第9个位开始,用8个无符号数替换已经存在的8个位,其实就是把第二个字符替换了,由e变成a(它的ASCII码是97),可以看到结果也变成了 hallo
127.0.0.1:6379> bitfield w set u8 8 97 1) (integer) 101 127.0.0.1:6379> get w "hallo"
incrby
incrby对指定范围的位进行自增操作,即++,这可能会发生溢出,如果增加了正数,会出现上溢出,如果增加的是负数,会出现下溢出,
redis默认的处理是折返,即如果出现了溢出,就将溢出的符号位丢掉,例如,如果是8位无符号数255,加1后就全部变成0,如果是8位有符号数127,加1后就溢出变成-128。
依然根据hello字符,来演示一下 incrby
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitfield w get u4 2 # 从第3位开始取4个无符号整数,第一次是10 1) (integer) 10 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 11 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 12 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 13 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 14 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 15 127.0.0.1:6379> bitfield w incrby u4 2 1 #到这里的时候,已经溢出折返成0了 1) (integer) 0
bitfield指令提供溢出策略子指令 overflow,用户可以选择溢出行为,默认是折返(wrap),还可以选择失败(fail) 即报错不执行,还有饱和截断(sat) 即超过范围就停留在最大或最小值,
overflow指令只影响接下来的第一条指令,这条指令执行完以后,溢出策略就会变成默认值 折返(wrap)。
饱和截断
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 11 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 12 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 13 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 14 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 15 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 # 接下来的都将是保持最大值 1) (integer) 15 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 15 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 15
失败不执行
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 11 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 12 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 13 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 14 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 15 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 # 接下来的都是失败 1) (nil) 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (nil) 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (nil)
get/set/incrby一起执行
127.0.0.1:6379> bitfield w set u4 1 0 get u4 1 incrby u4 2 1 1) (integer) 0 2) (integer) 0 3) (integer) 1 127.0.0.1:6379> get w "\x04ello"
更多编程相关知识,请访问:编程视频!!
以上がこの記事では、redis のビットマップ (ビットマップ) について説明します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。