Redis-BitMap

Redis задняя часть
Что такое растровое изображение

Значение или состояние, соответствующее элементу, представлено битом, а ключ — это сам соответствующий элемент. Битмапы сами по себе не являются структурой данных, на самом деле это строка (значение, соответствующее ключу, является последней строкой двоичного кода на приведенном выше рисунке), но он может работать с битами строки. Растровые изображения предоставляют отдельный набор команд, поэтому использование растровых изображений в Redis отличается от использования строк. Растровые изображения можно представить как массив битов.Каждая единица массива может хранить только 0 и 1. Нижний индекс массива называется смещением в растровых изображениях.


Растровые команды

SETBIT

Синтаксис: значение смещения ключа SETBIT

инструкция:

правильноkeyСохраненное строковое значение, которое устанавливает или очищает бит по указанному смещению.

бит устанавливается или сбрасывается в зависимости отvalueпараметр, который может быть0так же может быть1.

когдаkeyЕсли он отсутствует, автоматически создается новое строковое значение.

Строка выращена, чтобы убедиться, что она можетvalueСохранить с указанным смещением. Когда строковое значение растягивается, пустая позиция начинается с0заполнение.

offsetАргумент должен быть больше или равен0, менее 2^32 (битовая карта ограничена 512 МБ).

использовать большиеoffsetизSETBITС точки зрения эксплуатации выделение памяти может привести к блокировке сервера Redis.

возвращаемое значение:

Строковое значение указывает бит по смещению, которое было изначально сохранено.

Пример:
# SETBIT 会返回之前位的值(默认是 0)这里会生成 126 个位
coderknock> SETBIT testBit 125 1
(integer) 0
coderknock> SETBIT testBit 125 0
(integer) 1
coderknock> SETBIT testBit 125 1
(integer) 0
coderknock> GETBIT testBit 125
(integer) 1
coderknock> GETBIT testBit 100
(integer) 0
# SETBIT  value 只能是 0 或者 1  二进制只能是0或者1
coderknock> SETBIT testBit 618 2
(error) ERR bit is not an integer or out of range

получить значение

GETBIT


Синтаксис: смещение ключа GETBIT
инструкция:

правильноkeyСохраненное строковое значение получает бит с указанным смещением.

когдаoffsetбольше, чем длина строкового значения, илиkeyЕсли он не существует, верните0.

возвращаемое значение:

Строковое значение указывает бит по смещению.

Пример:
coderknock> EXISTS bit
(integer) 0
coderknock> SETBIT bit 125 1
(integer) 0
coderknock> GETBIT bit 125
(integer) 1
# 偏移量如果不存在则是0
coderknock> GETBIT bit 126
(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 "

Получить количество битов в указанном диапазоне растровых изображений со значением 1

BITCOUNT


Синтаксис: запуск ключа BITCOUNT
инструкция:

Вычисляет заданную строку, для которой задано значение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 126
(integer) 1

Несколько операций с растровыми изображениями

BITOP


Синтаксис: BITOP-операция destkey key [key ...]
инструкция:

для одной или нескольких строк, содержащих двоичные биты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Возьмите логическое XOR и сохраните результат в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).При работе с большими матрицами или статистикой больших объемов данных лучше всего назначать задачи подчиненным узлам, чтобы избежать блокировки главного узла.

Вычислить смещение первого значения в растровых изображениях

BITPOS

Доступно с версии 2.8.7.

временная сложность:НА).

Синтаксис: бит ключа BITPOS [начало][конец]
инструкция:

Возвращает первый бит в строке, для которой установлено значение 1 или 0.

Возвращает позицию, обрабатывая строку как массив байтов слева направо, причем первый подходящий элемент находится в позиции 0, второй — в позиции 8 и т. д.

GETBITиSETBITАналогичная команда для управления байтовыми битами.

По умолчанию вся строка извлекается один раз, и только когда указаны начальный и конечный параметры (возможно указание начального и конечного битов), диапазон интерпретируется как диапазон байтов, а не последовательность битов. такstart=0иend=2Это относится к поиску в первых трех байтах.

Обратите внимание, что возвращаемые битовые позиции всегда отсчитываются от 0, даже если start используется для указания начального байта.

иGETRANGEКак и команды, start и end также могут содержать отрицательные значения.Отрицательные значения будут отсчитываться от конца строки, -1 — это последний байт строки, -2 — предпоследний и так далее.

Несуществующий ключ будет рассматриваться как пустая строка.

возвращаемое значение:

Команда возвращает первый бит строки, для которой установлено значение 1 или 0.

Если мы ищем что-то, чей бит равен 1, в пустой строке или в 0-байтовой строке, результат вернет -1.

Если мы ищем значение в строке, где бит равен 0, а строка содержит только 1, возвращается первый пустой бит в крайнем правом углу строки. Если есть строка, значение которой равно трем байтам0xffстрока, затем командаBITPOS key 0вернет 24, потому что биты 0-23 равны 1.

По сути, мы можем думать о строках как о бесконечном числе нулей справа.

Однако если вы используете указанные начальный и конечный диапазоны для поиска указанного значения, если в диапазоне нет соответствующего значения, результатом будет -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

BITFIELD

Доступно с версии 3.2.0.

временная сложность:Сложность каждой подкоманды составляет O(1).

Синтаксис: ключ BITFIELD [смещение типа GET][значение смещения типа SET][приращение смещения типа INCRBY][OVERFLOW WRAP|SAT|FAIL]
инструкция:

BITFIELD key GET type offset INCRBY type offset increment

                                                                                  `BITFIELD` 命令可以将一个 Redis 字符串看作是一个由二进制位组成的数组, 并对这个数组中储存的长度不同的整数进行访问 (被储存的整数无需进行对齐)。 换句话说, 通过这个命令, 用户可以执行诸如 “对偏移量 1234 上的 5 位长有符号整数进行设置”、 “获取偏移量 4567 上的 31 位长无符号整数”等操作。 此外, `BITFIELD` 命令还可以对指定的整数执行加法操作和减法操作, 并且这些操作可以通过设置妥善地处理计算时出现的溢出情况。

BITFIELDКоманда может работать с несколькими битовыми диапазонами одновременно в одном вызове: она принимает последовательность операций для выполнения в качестве параметров и возвращает массив в качестве ответа, где каждый элемент массива является результатом соответствующей операции.

Например, следующая команда показывает, как добавить 8-разрядное целое число со знаком по смещению 100 и получить 4-разрядное целое число без знака по смещению 0:

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-битные целые числа без знака.

Биты и смещения позиций

В команде битового диапазона у пользователя есть два способа установить смещение:

  • Если пользователь дает число без префикса, то это число указывает на смещение строки от нуля.

  • С другой стороны, если пользователь дает#Смещение префикса, затем команда будет использовать это смещение для умножения битовой длины числового типа, установленного для вычисления реального смещения.

Например, для следующей команды:

BITFIELD mystring SET i8 #0 100 i8 #1 200

команда будетmystringвнутри ключа, первыйi8Значение длины в битах установлено равным100, и поставить второйi8Значение длины в битах установлено равным200. Когда мы используем строковый ключ в качестве массива, и массив хранит все целые числа одинаковой длины, используйте#Префиксы избавляют нас от необходимости вручную вычислять, где установлены биты.

контроль переполнения

Пользователи могут проходитьOVERFLOWкоманду, наряду с тремя параметрами, показанными ниже, укажитеBITFIELDПоведение команды при возникновении состояния переполнения или потери значимости при выполнении операции автоинкремента или автодекремента:

  • WRAP: Используйте циклический метод для обработки условий переполнения для целых чисел со знаком и без знака. Для целых чисел без знака перенос похож на выполнение вычисления по модулю с использованием самого значения и наибольшего целого числа без знака, которое можно сохранить, что также является стандартным поведением в C. Для целых чисел со знаком переполнение приведет к пересчету числа из наименьшего отрицательного числа, а потеря значимости приведет к пересчету числа из наибольшего положительного числа. Например, если мы127изi8Добавьте единицу к целому числу, и вы получите результат-128.

  • SAT: Используйте арифметику насыщения для обработки переполнения, то есть результатом вычисления потери значимости является наименьшее целочисленное значение, а результатом вычисления переполнения является наибольшее целочисленное значение. Например, если мы возьмем значение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 первого байта растрового изображения старшим битом и так далее. Например, если мы устанавливаем растровое изображение, которое было предварительно установлено на все 0, установите его значение по смещению 7 на 5-битное целое число без знака 23 (двоичные биты10111), то команда создаст следующее растровое представление:

+--------+--------+
|00000001|01110000|
+--------+--------+

Когда смещение и целочисленная длина выравниваются по границам байтов,BITFIELDБиты представлены так же, как и с обратным порядком байтов, но также важно понимать, как располагаются биты при отсутствии выравнивания.

возвращаемое значение:

если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    

кейс

Сценарий 1. Вход пользователя

Jedis redis = new Jedis("192.168.31.89",6379,100000);
//用户uid
String uid = "1";
String cacheKey = "sign_"+Integer.valueOf(uid);
//记录有uid的key
// $cacheKey = sprintf("sign_%d", $uid);

//开始有签到功能的日期
String startDate = "2017-01-01";

//今天的日期
String todayDate = "2017-01-21";

//计算offset(时间搓)
long startTime = dateParase(startDate,"yyyy-MM-dd").getTime();
long todayTime = dateParase(todayDate,"yyyy-MM-dd").getTime();
long offset = (long) Math.floor((todayTime - startTime) / 86400);

System.out.println("今天是第"+offset+"天");

//签到
//一年一个用户会占用多少空间呢?大约365/8=45.625个字节,好小,有木有被惊呆?
redis.setbit(cacheKey,offset,"1");

//查询签到情况
boolean bitStatus = redis.getbit(cacheKey, offset);
//判断是否已经签到
//计算总签到次数
long qdCount = redis.bitcount(cacheKey);

Сценарий 2: подсчет активных пользователей

Используйте время как cacheKey, тогда идентификатор пользователя смещается, если он был активен в этот день, установите его на 1 Поэтому, если я вычисляю активных пользователей определенного дня/месяца/года (на данный момент только один день в сети в течение статистического периода называется активным), пожалуйста, дайте следующую команду redis Команда BITOP операция destkey key [клавиша ...] Описание: выполнить побитовую операцию над одним или несколькими строковыми ключами, содержащими двоичные биты, и сохранить результат в destkey. Описание: Команда BITOP поддерживает любую из четырех операций AND, OR, NOT, XOR.

Map<String,List<Integer>>dateActiveuser = new HashMap<>();
Jedis redis = new Jedis("192.168.31.89",6379,100000);
Integer[] temp01 = {1,2,3,4,5,6,7,8,9,10};
List<Integer>temp01List = new ArrayList<>();
Collections.addAll(temp01List,temp01);
dateActiveuser.put("2017-01-10",temp01List);


Integer[] temp02 = {1,2,3,4,5,6,7,8};
List<Integer>temp02List = new ArrayList<>();
Collections.addAll(temp02List,temp02);
dateActiveuser.put("2017-01-11",temp02List);

Integer[] temp03 = {1,2,3,4,5,6};
List<Integer>temp03List = new ArrayList<>();
Collections.addAll(temp03List,temp03);
dateActiveuser.put("2017-01-12",temp03List);

Integer[] temp04 = {1,4,5,6};
List<Integer>temp04List = new ArrayList<>();
Collections.addAll(temp04List,temp04);
dateActiveuser.put("2017-01-13",temp04List);

Integer[] temp05 = {1,4,5,6};
List<Integer>temp05List = new ArrayList<>();
Collections.addAll(temp05List,temp05);
dateActiveuser.put("2017-01-14",temp05List);

String date[] = {"2017-01-10","2017-01-11","2017-01-12","2017-01-13","2017-01-14"};

//测试数据放入redis中
for (int i=0;i<date.length;i++){
    for (int j=0;j<dateActiveuser.get(date[i]).size();j++){
        redis.setbit(date[i], dateActiveuser.get(date[i]).get(j), "1");
    }
}

//bitOp
redis.bitop(BitOP.AND, "stat", "stat_2017-01-10", "stat_2017-01-11","stat_2017-01-12");

System.out.println("总活跃用户:"+redis.bitcount("stat"));

redis.bitop(BitOP.AND, "stat1", "stat_2017-01-10", "stat_2017-01-11","stat_2017-01-14");
System.out.println("总活跃用户:"+redis.bitcount("stat1"));

redis.bitop(BitOP.AND, "stat2", "stat_2017-01-10", "stat_2017-01-11");
System.out.println("总活跃用户:"+redis.bitcount("stat2"));