содержание
- содержание
- Основное введение в растровые изображения
- Растровое изображение в Java
- Растровые изображения в Redis
- Сценарии применения
- Суммировать
- Справочная статья
Основное введение в растровые изображения
концепция
Что такое растровое изображение?Битмап, все дословно переводят его как растровое.Мое понимание такое: битмап - это непрерывный двоичный бит (бит) в памяти, который можно использовать для дедупликации и статистики по большому количеству целых чисел.
Представьте маленький каштан, чтобы помочь понять:
Предположим, мы хотим сохранить три числа int(1,3,5), в java мы используем для хранения массив int, тогда он занимает байт 12. Но если мы применим битовый массив, и позиция соответствующего нижнего индекса равна 1, он также может представлять то же значение, например
индекс | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
двоичное значение | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
Видно, что соответствует1,3,5Значение бита нижнего индекса равно 1, и мы или компьютер также можем его получить.1,3,5этой информации.
Преимущество
Итак, в чем польза от этого? Это кажется более хлопотным. Следующий метод хранения применяется дляbit[8]
В этом сценарии занят только один байт, а занятая память составляет 1/12 от исходной.Когда объем данных огромен, например, 4 миллиарда целых чисел, сохраненная память в это время составляет более 10 гигабайт памяти.
Это представляет первое преимущество растровых изображений, которые занимают меньше памяти.
Подумайте еще раз, добавьте, что теперь у нас есть растровое изображение, содержащее данные регистрации пользователя на сегодняшний день.Нижний индекс может быть идентификатором пользователя.
A:
Идентификатор пользователя | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
двоичное значение | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
Это означает, что пользователь (1,3,5) зарегистрировался сегодня.
И, конечно же, битмап со вчерашнего дня,
B:
Идентификатор пользователя | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
двоичное значение | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 |
Это означает, что пользователь (1,2,3,7) зарегистрировался вчера.
Теперь мы хотим попросить:
- Пользователи, которые вошли вчера и сегодня.
- Пользователи, которые вошли вчера или сегодня.
Если он хранится в реляционной базе данных, это будет громоздкая операция, либо запись некоторых операторов SQL с неоднозначным значением, либо выполнение двух запросов, а затем двойной цикл в памяти для оценки.
А использовать растровые изображения очень просто,A & B
, A | B
Вышеприведенная операция, очевидно, является набором与或
операции, а двоичный код естественным образом поддерживает логические операции, иИзвестно, что кошки жидкие.Неправильно, хорошо известно, что компьютеры очень эффективны в бинарных операциях.
Это второе преимущество растровых изображений: они поддерживают операцию "и-или" и очень эффективны.
вау так идеально,Итак, где вы можете купить его?, так какие недостатки?
недостаточный
Конечно, растровые изображения нелегко поддерживать.非运算
, (конечно, реляционные базы данных плохо поддерживаются). Это предложение может быть немного трудным для понимания. Продолжайте приводить пример:
Если мы хотим запросить пользователей, которые не зарегистрировались сегодня, невозможно напрямую отменить растровое изображение.
Результат, полученный путем инвертирования растрового изображения, подписанного сегодня, выглядит следующим образом:
Идентификатор пользователя | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
двоичное значение | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 |
Значит ли это, что пользователь не заходил сегодня (0,2,4,6,7)?Нет, пользователя с номером 7 (любой номер) нет, или он вышел из системы.
Это связано с тем, что растровые изображения могут представлять только логическую информацию, т.е.true/false
.Он находится в этом растровом изображении, что означаетXX пользователь вошел сегодня или не входил, но не может выразить дополнительно,xx用户存在/不存在这个状态
.
Но мы можем спасти страну криво, сначала сделать битмап полного набора пользователей.Например:
Полные работы:
Идентификатор пользователя | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
двоичное значение | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
Затем используйте битовую карту полного набора и битовую карту регистрации, чтобы сделать операцию XOR, это же 0, а разница 1.
Логика в бизнесе такова: присутствие пользователя и регистрация два логических значения, всего четыре комбинации.
- Пользователь существует и зарегистрирован. Соответствующие биты обоих наборов равны 1, тогда результат равен 0.
- Пользователь существует, но не регистрируется. Соответствующий бит полного набора равен 1, а регистрация равна 0, поэтому результат равен 1.
- Если пользователь не существует, то регистрация должна быть невозможна. Соответствующие биты двух наборов равны 0, и результат равен 0.
Таким образом, в результате есть только одна возможность для 1:Пользователь существует и не авторизован, это именно то, что мы хотим.
A^ Полное собрание сочинений:
Идентификатор пользователя | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
двоичное значение | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
К тому же производительность битмапов для разреженных данных не очень,(разумеется, умные начальники в основном решили эту проблему).Для нативных битмапов,если у нас всего два пользователя,1 и 100000000 пользователей,Тогда непосредственно хранение int требует 8 байт, то есть 32 бита, а для хранения растровых изображений требуется 100 миллионов бит.量少,且跨度极大
То есть при разрежении собственные растровые изображения не подходят.
Нажмите здесь, чтобы перейти к稀疏数据
решение
Суммировать
Итак, подведем итоги:
Bitmap – это структура данных, в которой для хранения целочисленных данных используются двоичные биты. Она имеет множество применений, особенно в сценариях с большими объемами данных, очень практична для экономии памяти и повышения эффективности вычислений.
Его преимущества:
- Экономьте память. -> Следовательно, это более важно, когда объем данных велик.
- И или операция эффективна -> Можно быстро найти пересечение и объединение.
Недостатки:
- Неоперация не может быть выполнена напрямую -> Основная причина заключается в том, что растровое изображение может хранить только одну логическую информацию, и если информации больше, необходимо использовать помощь с данными, такую как полная коллекция.
- Когда данных мало, пространство тратится впустую. -> Не беспокойтесь об этом, мы поговорим о решениях больших парней позже, которые в принципе можно решить.
- Можно хранить только логические типы. -> Существуют ограничения, но многие данные в бизнесе могут быть преобразованы в логические типы. Например, в приведенном выше примеределовое намерение: запись о ежедневных проверках пользователя с пользователем в качестве параметра.Мы можем преобразовать в: Булев тип данных, регистрируется ли каждый пользователь каждый день.
Растровое изображение в Java
Принцип растрового изображения описан выше, поэтому давайте сначала реализуем его вручную!
рудиментарная версия
Примечание: поскольку версия JDK будет позже, здесь реализована только очень грубая версия, которая удобна для понимания основного принципа растрового изображения.совершенно невозможноЕго можно использовать напрямую и запускать, но во многих случаях он будет напрямую сообщать об ошибке.
Хотя это просто, необходимо иметь его.
Метод строительства
Напишите параметр построения, который поддерживает только число битов. Поскольку мы используем массив целых чисел для сохранения фактических данных, входящее значение右移5
(эквивалентно делению на 32, поскольку int — 32 бита) — это размер массива int.
установить метод
Поддерживает настройку бита на true/false.
Чтобы достичьset-true
, на самом деле есть грубая логика в соответствии с человеческим мышлением, например, при вызовеset(5,true)
Когда мы преобразуем целое число в двоичную строку, мы получаем000000000000000000000000000000
(должно быть 32, я не считал), затем устанавливаем шестую позицию справа на 1, и получаем000000000000000000000000100000
, а затем преобразовать обратно в целое число.
Этот метод подходитбитовая картаПрямое определение , тоже понятно, но для компьютера это слишком хлопотно, да и в процессе нужна String, которая занимает слишком много места в памяти.
Компьютеры предпочитают использовать для решения операцию ИЛИ Предположим, существующее число равно 3, т.е.000000000000000000000000001000
, то звонимset(10,true)
, как это сделать, сначала используйте сдвиг влево, установите 11-ю позицию в 1, а затем выполните операцию ИЛИ с исходным значением, как показано ниже:
原来值 : 000000000000000000000000001000
1右移10位: 000000000000000000010000000000
或操作的结果: 000000000000000000010000001000 ----> 可以直接表示 3 和 10 两个位上都为1了.
Установка определенного бита в false отличается от описанного выше процесса.В дополнение к грубому методу вы также можете1右移x位
из非
Выбирать与
.Это полный рот, вот пример:
Ставим 0 на 3.
原来值 : 000000000000000000010000001000 ----> 10和3上为1,
1右移3位: 000000000000000000000000001000
1右移3位取非后: 111111111111111111111111110111
原来的值与取非后取与: 000000000000000000010000000000 ----> 只有10上为1了.
получить метод
Получите значение немного.
Конечно, ее также можно решить грубым преобразованием двоичных строк, но используя与操作
Быстрее и удобнее для компьютера.
Для примера в методе SET, после настройки 3 и 10, если вы получите значение 10, вы можете:
当前值: 000000000000000000010000001000
1右移10位: 000000000000000000010000000000
与操作的结果: 000000000000000000010000000000 ---> 只要这个数字不等于0,即说明10上为1,等于0则为0.
Фактический код аннотируется следующим образом:
/**
* Created by pfliu on 2019/07/02.
*/
public class BitMapTest {
// 实际使用int数组存储
private int[] data;
/**
* 构造方法,传入预期的最大index.
*/
public BitMapTest(int size) {
this.data = new int[size >> 5];
}
/**
* get 方法, 传入要获取的index, 返回bool值代表该位上为1/0
*/
public boolean get(int bitIdx) {
return (data[bitIdxToWorkIdx(bitIdx)] & (1 << bitIdx)) != 0;
}
/**
* 将对应位置的值设置为传入的bool值
*/
public void set(int idx, boolean v) {
if (v) {
set(idx);
} else {
clear(idx);
}
}
// 将index的值设置为1
private void set(int idx) {
data[bitIdxToWorkIdx(idx)] |= 1 << idx;
}
// 将index上的值设置为0
private void clear(int bitIdx) {
data[bitIdxToWorkIdx(bitIdx)] &= ~(1L << bitIdx);
}
// 根据bit的index获取它存储的实际int在数组中的index
private int bitIdxToWorkIdx(int bitIdx) {
return bitIdx >> 5;
}
public static void main(String[] args) {
BitMapTest t = new BitMapTest(100);
t.set(10, true);
System.out.println(t.get(9));
System.out.println(t.get(10));
}
}
Версия JDK (чтение исходного кода BitSet)
Растровое изображение реализовано в JDK, а класс реализацииBitSet
, общая идея аналогична рудиментарному варианту, реализованному выше, за исключением того, что его внутренние данные хранятся в длинном массиве, и добавлено много отказоустойчивой обработки.Заглянем в исходный код.Он по-прежнему классифицируется по к методу.
Константы и переменные
// long数组,64位的long是2的6次方
private final static int ADDRESS_BITS_PER_WORD = 6;
// 每一个word的bit数量
private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
// 存储数据的long数组
private long[] words;
// 上面的数组中使用到了的word的个数
private transient int wordsInUse = 0;
// 数组的大小是否由用户指定的(注释里写明了:如果是true,我们假设用户知道他自己在干什么)
private transient boolean sizeIsSticky = false;
Конструктор и фабричные методы
BitSet предоставляет два общедоступных конструктора и четыре общедоступных фабричных метода, поддерживающих соответственноlong[]
,LongBuffer
,bytes []
, ByteBuffer
Получите экземпляр BitSet в формате .
Различные методы и методы, которые они вызывают внутри, следующие:
// ---------构造方法-------
// 无参的构造方法,初始化数组为长度为64个bit(即一个long)以及设置sizeIsSticky为false.
public BitSet() {
initWords(BITS_PER_WORD);
sizeIsSticky = false;
}
// 根据用户传入的bit数量进行初始化,且设置sizeIsSticky为true.
public BitSet(int nbits) {
// nbits can't be negative; size 0 is OK
if (nbits < 0)
throw new NegativeArraySizeException("nbits < 0: " + nbits);
initWords(nbits);
sizeIsSticky = true;
}
// ---------构造方法的调用链 -------
// 初始化数组
private void initWords(int nbits) {
words = new long[wordIndex(nbits-1) + 1];
}
// 根据bit数量获取long数组的大小,右移6位即可.
private static int wordIndex(int bitIndex) {
return bitIndex >> ADDRESS_BITS_PER_WORD;
}
// ---------工厂方法,返回BitSet实例 -------
// 传入long数组
public static BitSet valueOf(long[] longs) {
int n;
for (n = longs.length; n > 0 && longs[n - 1] == 0; n--)
;
return new BitSet(Arrays.copyOf(longs, n));
}
// 传入LongBuffer
public static BitSet valueOf(LongBuffer lb) {
lb = lb.slice();
int n;
for (n = lb.remaining(); n > 0 && lb.get(n - 1) == 0; n--)
;
long[] words = new long[n];
lb.get(words);
return new BitSet(words);
}
// 传入字节数组
public static BitSet valueOf(byte[] bytes) {
return BitSet.valueOf(ByteBuffer.wrap(bytes));
}
// 传入ByteBuffer
public static BitSet valueOf(ByteBuffer bb) {
bb = bb.slice().order(ByteOrder.LITTLE_ENDIAN);
int n;
for (n = bb.remaining(); n > 0 && bb.get(n - 1) == 0; n--)
;
long[] words = new long[(n + 7) / 8];
bb.limit(n);
int i = 0;
while (bb.remaining() >= 8)
words[i++] = bb.getLong();
for (int remaining = bb.remaining(), j = 0; j < remaining; j++)
words[i] |= (bb.get() & 0xffL) << (8 * j);
return new BitSet(words);
}
установить метод
BitSet предоставляет два типа методов установки:
- Набор одной точки Установите индекс на tue/false.
- Задан диапазон. Установите значение диапазона tue/false.
Таким образом, BitSet имеет четыре перегруженных метода set.
// 将某个index的值设置为true. 使用和上面自己实现的简陋版本相同的或操作.
public void set(int bitIndex) {
if (bitIndex < 0)
throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
int wordIndex = wordIndex(bitIndex);
expandTo(wordIndex);
words[wordIndex] |= (1L << bitIndex); // Restores invariants
checkInvariants();
}
// 将某个index设置为传入的值,注意当传入值为false的时候,调用的是clear方法.
public void set(int bitIndex, boolean value) {
if (value)
set(bitIndex);
else
clear(bitIndex);
}
// 将index上bit置为0
public void clear(int bitIndex) {
if (bitIndex < 0)
throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
int wordIndex = wordIndex(bitIndex);
if (wordIndex >= wordsInUse)
return;
words[wordIndex] &= ~(1L << bitIndex);
recalculateWordsInUse();
checkInvariants();
}
// 将from->to之间的所有值设置为true
public void set(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
if (fromIndex == toIndex)
return;
// Increase capacity if necessary
int startWordIndex = wordIndex(fromIndex);
int endWordIndex = wordIndex(toIndex - 1);
expandTo(endWordIndex);
long firstWordMask = WORD_MASK << fromIndex;
long lastWordMask = WORD_MASK >>> -toIndex;
if (startWordIndex == endWordIndex) {
// Case 1: One word
words[startWordIndex] |= (firstWordMask & lastWordMask);
} else {
// Case 2: Multiple words
// Handle first word
words[startWordIndex] |= firstWordMask;
// Handle intermediate words, if any
for (int i = startWordIndex+1; i < endWordIndex; i++)
words[i] = WORD_MASK;
// Handle last word (restores invariants)
words[endWordIndex] |= lastWordMask;
}
checkInvariants();
}
// 将from->to之间的所有值设置为传入的值,当传入的值为false的适合,调用的是下面的clear.
public void set(int fromIndex, int toIndex, boolean value) {
if (value)
set(fromIndex, toIndex);
else
clear(fromIndex, toIndex);
}
// 将范围内的bit置为0
public void clear(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
if (fromIndex == toIndex)
return;
int startWordIndex = wordIndex(fromIndex);
if (startWordIndex >= wordsInUse)
return;
int endWordIndex = wordIndex(toIndex - 1);
if (endWordIndex >= wordsInUse) {
toIndex = length();
endWordIndex = wordsInUse - 1;
}
long firstWordMask = WORD_MASK << fromIndex;
long lastWordMask = WORD_MASK >>> -toIndex;
if (startWordIndex == endWordIndex) {
// Case 1: One word
words[startWordIndex] &= ~(firstWordMask & lastWordMask);
} else {
// Case 2: Multiple words
// Handle first word
words[startWordIndex] &= ~firstWordMask;
// Handle intermediate words, if any
for (int i = startWordIndex+1; i < endWordIndex; i++)
words[i] = 0;
// Handle last word
words[endWordIndex] &= ~lastWordMask;
}
recalculateWordsInUse();
checkInvariants();
}
Здесь следует отметить момент, когда входящее значениеtrue/fasle
Когда логика обработки отличается. Конкретную логику см. в примере в грубой версии выше.
получить метод
BitSet предоставляет метод для получения значения бита одной позиции и метод для получения диапазона, который возвращает новый BitSet.
// 获取某个位置的bit值
public boolean get(int bitIndex) {
if (bitIndex < 0)
throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
checkInvariants();
int wordIndex = wordIndex(bitIndex);
return (wordIndex < wordsInUse)
&& ((words[wordIndex] & (1L << bitIndex)) != 0);
}
// 返回一个子集,包含传入范围内的bit
public BitSet get(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
checkInvariants();
int len = length();
// If no set bits in range return empty bitset
if (len <= fromIndex || fromIndex == toIndex)
return new BitSet(0);
// An optimization
if (toIndex > len)
toIndex = len;
BitSet result = new BitSet(toIndex - fromIndex);
int targetWords = wordIndex(toIndex - fromIndex - 1) + 1;
int sourceIndex = wordIndex(fromIndex);
boolean wordAligned = ((fromIndex & BIT_INDEX_MASK) == 0);
// Process all words but the last word
for (int i = 0; i < targetWords - 1; i++, sourceIndex++)
result.words[i] = wordAligned ? words[sourceIndex] :
(words[sourceIndex] >>> fromIndex) |
(words[sourceIndex+1] << -fromIndex);
// Process the last word
long lastWordMask = WORD_MASK >>> -toIndex;
result.words[targetWords - 1] =
((toIndex-1) & BIT_INDEX_MASK) < (fromIndex & BIT_INDEX_MASK)
? /* straddles source words */
((words[sourceIndex] >>> fromIndex) |
(words[sourceIndex+1] & lastWordMask) << -fromIndex)
:
((words[sourceIndex] & lastWordMask) >>> fromIndex);
// Set wordsInUse correctly
result.wordsInUse = targetWords;
result.recalculateWordsInUse();
result.checkInvariants();
return result;
}
логическая операция
Конечно, растровое изображение, реализованное JDK, имеет логические операции, которые в основном поддерживают与,或,异或,与非
Операций четыре.Поскольку код не сложный, код сюда выкладывать не буду, а API выложу кратко.
// 与操作
public void and(BitSet set);
// 或操作
public void or(BitSet set);
// 异或操作
public void xor(BitSet set);
// 与非操作
public void andNot(BitSet set);
На данный момент исходный код BitSet прочитан, но нашли ли вы проблему?稀疏数据
Проблема не решена, не переживайте, придет следующее.
EWAHCompressedBitmap
Это класс в пакете javaEWAH, разработанный Google.EWAH = Enhanced Word-Aligned Hybrid
.И Сжатый означает сжатый.
обзор稀疏数据
Проблема, предполагая, что мы находимся в растровом изображении, сначалаset(1)
,Потомset(1亿)
Что случится?
Давайте воспользуемся BitSet в JDK, чтобы опробовать его, и разобьем точку во время запущенного процесса, чтобы увидеть, как выглядит внутренний массив, как показано ниже:
Сериализуйте его и выведите в файл, размер файла следующий:
Видно, что для сохранения двух чисел 1 и 100 млн мы потратили длинный массив длиной более 10 млн, который после сериализации занимает почти 200м памяти, это ненаучно.
ДалееEWAHCompressedBitmap
Да, в названии есть сжатие, так что должно быть оно хорошо зарекомендовало себя.
Вы можете видеть, что длина длинного массива всего 4, а размер выходного файла — 96 байт.
Это ожидаемо.
В EWAHCompressedBitmap данные также хранятся в виде длинного массива, но для каждого длинного,Literal Word
иRunning Length Word
.
-
Literal Word
: сохранить реальный бит. -
Running Length Word
: Сохранение информации о диапазоне.
Что такое информация о промежутке? Например:
При использовании BitSet для хранения 100 миллионов только что в длинном массиве на снимке экрана более 10 миллионов нулей, а после этого значение.
Используйте BitSet для хранения 1 и 100 миллионов (2048 — фиктивное значение, не забудьте его):
long | long | long | long | long | long | long | long | long | long | long | long |
---|---|---|---|---|---|---|---|---|---|---|---|
2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... 10 миллионов нулей | 0 | 0 | 2048 |
В EWAHCompressedBitmap это выглядит так:
long | long | long |
---|---|---|
2 | десять миллионов нулей | 2048 |
Может показаться, что это не имеет значения.... Но в BitSet 10 миллионов нулей действительно хранятся с использованием 10 миллионов длинных значений, а в EWAHCompressedBitmap эта информация хранится с использованием длинных значений, и значение long указывает, сколько нулей находится в этом интервале.
Делайте так, нажимайте на вопрос.Соответствует сжатию в названии.Сжимайте последовательные 0 или 1, чтобы сэкономить место.
Есть ли побочные эффекты от этого? Да, когда каждая из ваших вставок находится вRunning Length Word
, то есть каждая вставка включаетRunning Length Word
Разделение ухудшит производительность, поэтому официальная рекомендация — вставлять лучшие данные от малого к большему.
EWAHCompressedBitmap в основном решает проблему разреженных данных.Когда данные очень плотные, степень их сжатия не так хороша, но обычно она не хуже, чем метод несжатого хранения.Поэтому рекомендуется использовать этот класс в повседневном использовании. , если вы не очень ясны и можете гарантировать, что ваши данные не будут слишком большимиредкий.
Суммировать
В этом разделе мы вручную реализуем очень простое растровое изображение, а затем читаем класс реализации растрового изображения в JDK.BitSet
Исходный код, а затем проанализируйте, как использоватьEWAHCompressedBitmap
Чтобы решить проблему разреженных данных, дляEWAHCompressedBitmap
Конкретная реализация исходного кода подробно не анализировалась, и заинтересованные друзья могут проверить это самостоятельно.
Язык Java имеет широкий круг пользователей, поэтому в Интернете существуют различные версии для реализации растровых изображений, в том числе версии с открытым исходным кодом, поддерживаемые крупными производителями, и версии, написанные отдельными лицами. можно использовать различные фокусы.В модифицированном варианте, поскольку логика реализации растрового изображения не особо сложна, достаточно знать конкретную логику его реализации перед использованием.
Растровые изображения в Redis
Это очень короткое введение в растровые изображения на официальном сайте Redis....
Redis поддерживает растровые изображения, но растровые изображения — это не отдельная структура данных, а набор бит-ориентированных операционных инструкций, определенных для типа String. То есть, когда вы используете растровые изображения Redis, базовое хранилище фактически является строковым типом Redis. Поэтому:
- Поскольку строки Redis безопасны для двоичных файлов, их можно использовать в качестве хранилища растровых изображений.
- Максимальный тип String в Redis — 512 МБ, поэтому одно растровое изображение Redis может хранить только от 2 до 32 целых чисел. Этого должно быть достаточно (если этого недостаточно, вы можете разделить ключ и использовать для этого префикс).
- Поскольку нижний слой представляет собой строку, Redis не обрабатывает разреженные данные, поэтому обратите на это особое внимание при его использовании, чтобы этот ключ не перетаскивал сервер Redis.
Операции, поддерживаемые Redis, следующие:
-
getbit: получить значение определенной позиции ключа.
getbit key offset
. -
setbit: Установите значение позиции.
setbit key offset value
. -
bitcount: вычислить количество битов, равных 1 в ключе.Поддерживаемый диапазон.
bitcount key start end
-
bitpos: возвращает позицию первого указанного бита в диапазоне.
bitpos key bit(0/1) start end
-
bitop: Логическая операция, поддерживает четыре логические операции, и вышеперечисленные
BitSet
Четыре поддерживаемых одинаковы, а конкретные команды следующие:
BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP NOT destkey srckey
Где destkey — ключ хранилища результата, а остальное srckey — источник участия в операции.
Сценарии применения
Сценарий приложения на самом деле очень сложен, и вы не можете применить то, чему научились.В индустрии программистов это в основном эквивалентно тому, чтобы не изучать это...
После собственного исследования и просмотра в Интернете, я примерно увидел некоторые сценарии применения, и написал их примерно, чтобы каждый мог понять и столкнуться с подобными сценариями в будущем.Вы можете думать о растровых изображениях и применять их!
Регистрация/покупка пользователя и другие ограничения
Пользователи могут войти в систему только один раз в день, и только один из них может быть приобретен во время акции. Эти требования приводят к запросу запроса.给定的id做没做过某事
.И вообще такой запрос не может принять задержку с проверкой библиотеки.Конечно после того, как вы один раз проверите библиотеку, напишите в redis:key = 2345 , value = 签到过了
, также достижимо, но объем памяти слишком велик.
И после использования растровых изображений, когда2345После того, как пользователь зарегистрировался/накинулся, вызовите его в Redissetbit 2019-07-01-签到 2345 1
То есть каждый раз, когда приходит запрос пользователя на регистрацию/покупку, ему нужно только выполнить соответствующий getbit, чтобы получить логическое значение того, следует ли выпускать или нет.
Запись таким образом может не только сэкономить место и ускорить доступ, но и предоставить некоторые дополнительные статистические функции, такие как вызовbitcount
Для подсчета общего количества чекинов сегодня и т. д. Статистическая скорость обычно лучше, чем у реляционных баз данных, и может использоваться для запросов к интерфейсу в реальном времени.
Пользовательские теги и другие данные
Большие данные уже очень распространены, и все делают портреты пользователей.В это время пользователей нужно классифицировать по тегам и хранить.Это удобно для последующих рекомендаций и других операций.
Структура данных о пользователях и тегах представляет собой хлопотную задачу, и производительность запроса может быть слишком низкой.В то же время часто требуются логические операции для нескольких тегов, например, кто является пользователем после 00 кому нравятся электронные товары, женщины и что такое пользователи, которые любят путешествовать и т. д., что вызовет трудности при обработке в реляционных базах данных.
Битмапы можно использовать для хранения, каждая метка хранится в виде битмапа (по логике можно реально разделить по хвостовому номеру и т.д.), и выполнять быструю статистику и расчеты в нужное время. как:
Пользователь | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
люблю путешествовать | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |
можно четко рассчитать,0,3,6
Пользователи любят путешествовать.
Пользователь | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
после 00 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
Пользователь0,1,6
Это после 00.
Затем возьмите И двух растровых изображений, чтобы получить пользователей Love Travel после 00 как0,6
.
Фильтр Блума
Это более известно, и больше информации об этом можно найтиПринцип фильтра Блума и его применение в дедупликации рекомендаций
Суммировать
Короче говоря, растровое изображение может эффективно и экономить место для хранения логических данных, связанных с идентификаторами пользователей. Обычные приложения можно использовать для дедупликации и статистики больших объемов данных. Для большего количества приложений развивайте свое воображение.
Справочная статья
Исходный код BitSet/EWAHCompressedBitmap
Заканчивать.
ChangeLog
2019-07-02 ЗавершеноВсе вышеизложенное является личными мыслями, если есть какие-либо ошибки, пожалуйста, исправьте их в комментариях.
Добро пожаловать на перепечатку, пожалуйста, подпишите и сохраните исходную ссылку.
Контактный адрес электронной почты: huyanshi2580@gmail.com
Дополнительные заметки об обучении см. в личном блоге ------>Хуян тен