существуют современное приложение, Вход user — это обычная функция. Обычно мы используем MySQL
База данных для хранения записей о регистрации пользователей. Однако,С количеством пользователей увеличивать Увеличивать,Записи в базе данных будут расти линейно со временем и количеством пользователей.,Это не только увеличивает нагрузку на хранилище,И это может повлиять на эффективность запросов. существуют стремления к более высокой эффективности хранения и производительности запросов по сценарию,MySQL
Возможно, это уже не лучший вариант.
В это время,Redis
из Bitmap
Структура данных особенно важна. использовать Redis Bitmap
,Мы не только можем значительно сократить занимаемое пространство для хранения,Он также может эффективно реализовывать сложную статистику поведения пользователей.,Например, количество последовательных дней входа в систему, статистика ежемесячных входов и т. д. Следующий,В этой статье подробно описано, какиспользовать Redis Bitmap
Достичь высокой эффективности из Вход статистическая функция пользователя.
Вы готовы? Приготовьте чашку любимого кофе и чая и узнайте, прочитав эту статью.
Redis
из Bitmap
,Также называется растровым изображением,это хранилищеи Обработка битов(bit
)изструктура данных。существовать Redis
середина,Bitmap
Это не независимый тип данных, а особый способ строкового типа. Вы можете обрабатывать двоичный Кусочек в конкретном из Заказсуществовать строковые данные. Redis
Максимальная длина средней строки издля 512
МБ, каждый байт имеет 8
бит, поэтому строка может хранить до 512 * 1024 * 1024 * 8 = 2^32
Единая позиция.
Bitmap
Основные сценарии применения следующие:
0
Указывает, что вход не выполнен,1
Показывает, что вы вошли в систему. Битовое изображение может быстро подсчитать количество последовательных дней регистрации, общее количество дней регистрации и т. д. пользователей.bitmap
Можно реализовать фильтр Блума,bitmap
Может использоваться для эффективного определения наличия элемента в наборе. Сопоставьте элементы с помощью нескольких хэш-функций, чтобы bitmap
из разных позиций быстро определить наличие элементов изсуществовать.Bitmap
Запишите, активен ли пользователь в определенный день. Например, когда пользователь посещает веб-сайт или приложение, соответствующий бит устанавливается в значение для. 1
,Бит статистики позволяет быстро подсчитать количество активных пользователей.Записи о входе указаны в годах.,пользователь,Соответствует растровому изображению(Bitmap
),Указывает статус регистрации пользователя в течение одного года.
key
издизайн:user:sign:%d:%d
,Первый заполнитель представляет год,Второй заполнитель представляет номер пользователя.bitmap
Ценность дизайна: Всего за один год 365 или 366 дней, поэтому нам нужно только bitmap
внутри и спереди 366 немного, это 0-365 Кусочек.Далее будут объединены Go
язык и Redis
Промежуточное программное обеспечение реализует следующие функции:
Следующей реализацией функции будет use. Go
код языка для демонстрации, поэтому сначала нам нужно его установить Go Redis
полагаться.
go get github.com/redis/go-redis/v9
Для достижения Входа useriz, нам нужно использовать Redis
из SETBIT
Заказ.
SETBIT
Заказиспользуется длянастраиватьили Прозрачный字符串ценитьсерединаизопределенный бит(bit
)ценить,Использование следующее:
SETBIT key offset value
bit
)из Кусочек置。Кусочекиз Кусочек置从 0 Начни считать.Пример кода:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go/redis-bitmap-sign/sign/main.go
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
func RedisClient() *redis.Client {
return redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
}
func main() {
rdb := RedisClient()
if rdb == nil {
panic("redis client is nil")
}
oldValue, err := rdb.SetBit(context.Background(), "user:2024:1", 0, 1).Result()
if err != nil {
panic(err)
}
if oldValue == 1 {
fmt.Println("Повторить регистрацию")
} else {
fmt.Println(oldValue) // 0, что указывает на то, что этот бит («бит») является значением до установки нового значения.
}
}
В приведенном выше примере кода мы вызываем Redis
Экземпляр клиентаиз SetBit
метод, будет key
для user:2024:1
Переписка bitmap
средний класс 0 Дизайн локациидля 1。Это означает ID
для 1 изпользовательсуществовать 2024-01-01 Зарегистрировался。SetBit
методиз返回ценитьдля该Кусочек(bit
)одеялонастраивать新ценить之前изценить。
Чтобы реализовать запрос Вход статус пользователя, нам нужно использовать Redis
из GETBIT
Заказ.
GETBIT
Заказиспользуется для Получать字符串ценитьсерединаизопределенный бит(bit
)изценить,Использование следующее:
GETBIT key offset
bit
)из Кусочек置。Кусочекиз Кусочек置从 0 Начни считать.Пример кода:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go/redis-bitmap-sign/sign-in-record/main.go
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
func RedisClient() *redis.Client {
return redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
}
func main() {
rdb := RedisClient()
if rdb == nil {
panic("redis client is nil")
}
value, err := rdb.GetBit(context.Background(), "user:2024:1", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(value) // 1
}
В приведенном выше примере кода мы вызываем Redis
Экземпляр клиентаиз GetBit
метод, получить key
для user:2024:1
Переписка bitmap
серединаиз第 0 Кусочекизценитьдля 1,Это означает ID
для 1 изпользовательсуществовать 2024-01-01 Уже авторизован.
Чтобы подсчитать количество чекинов за год, нам нужно использовать Redis
из BITFIELD
Заказ.
Redis
из BITFIELD
Заказэто очень мощныйиз Заказ,Это позволяет изучить различные операции на битовом уровне.,В том числе читать, устанавливать, увеличивать битовые поля. Этот заказ способен работать с битовым массивом, хранящимся в существующей строке.,и может рассматриваться как прямойсуществоватьна веревкеосуществлятьсложныйиз Кусочек操作。Использование следующее:
BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment]
Подробную информацию см.:Redis BITFIRLED Command
Пример кода:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go/redis-bitmap-sign/cumulative-sign/main.go
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/redis/go-redis/v9"
)
// RedisClient инициализация Redis клиент
func RedisClient() *redis.Client {
return redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
}
// GetCumulativeDays Получить Указанный год из Суммарного количества дней заезда
func GetCumulativeDays(ctx context.Context, rdb *redis.Client, userID int, year int, dayOfYear int) (int, error) {
key := fmt.Sprintf("user:%d:%d", year, userID)
segmentSize := 63
cumulativeDays := 0
bitOps := make([]any, 0)
for i := 0; i < dayOfYear; i += segmentSize {
size := segmentSize
if i+segmentSize > dayOfYear {
size = dayOfYear - i
}
bitOps = append(bitOps, "GET", fmt.Sprintf("u%d", size), fmt.Sprintf("#%d", i))
}
values, err := rdb.BitField(ctx, key, bitOps...).Result()
if err != nil {
return 0, fmt.Errorf("failed to get bitfield: %w", err)
}
for idx, value := range values {
if value != 0 {
size := segmentSize
if (idx+1)*segmentSize > dayOfYear {
size = dayOfYear % segmentSize
}
for j := 0; j < size; j++ {
if (value & (1 << (size - 1 - j))) != 0 {
cumulativeDays++
}
}
}
}
return cumulativeDays, nil
}
func main() {
rdb := RedisClient()
if rdb == nil {
log.Fatal("redis client is nil")
}
now := time.Now()
// Получить текущийиз года
year := now.Year()
// Получить Какое текущее число в этом году?
dayOfYear := now.YearDay()
// Предполагаемый пользователь ID для 1
userID := 1
cumulativeDays, err := GetCumulativeDays(context.Background(), rdb, userID, year, dayOfYear)
if err != nil {
log.Fatalf("failed to get cumulative days: %v", err)
}
fmt.Printf("%d Совокупное количество дней регистрации в году: %d\n", year, cumulativeDays)
}
Приведенный выше код реализует функцию Статистика общего количества дней в этом году. Процесс выглядит следующим образом:
Redis
Пример клиента: использовать redis.NewClient()
соединение метода Redis
на сервер и Получить Экземпляр клиента。year := now.Year()
Получать。dayOfYear := now.YearDay()
Получать。ID
для 1。ID
построить только одиниз Redis Key
,Форматдля пользователь:год:идентификатор пользователя
。BitField
Из Каждая операция может обрабатываться. Максимальная длина 63 бит, определение segmentSize := 63
для пакетной обработки данных для входа. Интервал представляет 63 День из статуса регистрации.BitField
Заказизпараметр: проходить Цикл будет идти от начала года до текущей даты.издни(dayOfYear
)разделениедля每段最多包含 63 Несколько интервалов на небе, динамическая постройка. BitField
Заказизпараметр。rdb.BitField()
методосуществлятьстроитьхорошийиз BitField
Заказ,返回一个包含Кусочек二进制Перепискадесятичное представлениеиз int64
Тип срез.&
операции и операции перемещения) для обнаружения ситуации регистрации каждый раз, когда 1 Примерно cumulativeDays
Увеличивать 1。Чтобы подсчитать статус регистрации в определенном месяце, нам также необходимо использовать Redis
из BITFIELD
Заказ.
Пример кода:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go/redis-bitmap-sign/monthly-sign/main.go
package main
import (
"context"
"errors"
"fmt"
"log"
"time"
"github.com/redis/go-redis/v9"
)
func RedisClient() *redis.Client {
return redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
}
func main() {
rdb := RedisClient()
if rdb == nil {
panic("redis client is nil")
}
now := time.Now()
// Получить текущийиз года
year := now.Year()
// Предполагаемый пользователь ID для 1
userID := 1
// Получить Текущий месяц из дней
days := time.Date(now.Year(), now.Month()+1, 1, 0, 0, 0, 0, now.Location()).Add(-24 * time.Hour).Day()
// Получить Какой день является началом этого месяца и этого года?
offset := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location()).YearDay()
signOfMonth, err := GetSignOfMonth(context.Background(), rdb, userID, year, days, offset)
if err != nil {
log.Fatal(err)
}
fmt.Println(signOfMonth)
}
func GetSignOfMonth(ctx context.Context, rdb *redis.Client, userID, year, days, offset int) ([]bool, error) {
typ := fmt.Sprintf("u%d", days)
key := fmt.Sprintf("user:%d:%d", year, userID)
s, err := rdb.BitField(ctx, key, "GET", typ, offset).Result()
if err != nil {
return nil, fmt.Errorf("failed to get bitfield: %w", err)
}
if len(s) != 0 {
signInBits := s[0]
signInSlice := make([]bool, days)
for i := 0; i < days; i++ {
signInSlice[i] = (signInBits & (1 << (days - 1 - i))) != 0
}
return signInSlice, nil
} else {
return nil, errors.New("no result returned from BITFIELD command")
}
}
Приведенный выше код реализует функцию Статистика заездов за текущий месяц. Процесс выглядит следующим образом:
redis.NewClient()
соединение метода至 Redis
сервер и получить экземпляр клиента。year := now.Year()
Получать。time.Date(now.Year(), now.Month()+1, 1, 0, 0, 0, 0, now.Location()).Add(-24 * time.Hour).Day()
рассчитать.time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location()).YearDay()
Получать。ID
:В примере предполагается, что пользователь ID для 1。Redis key
и BitField
Заказизпараметр:ID
построить только одиниз Redis Key
,Форматдля пользователь:год:идентификатор пользователя
。days
строить type
параметр fmt.Sprintf("u%d", days)
,Указывает операцию по ширине битового поля.BitField
Заказ:проходить rdb.BitField()
методосуществлять BitField
Заказ,返回一个包含Кусочек二进制Перепискадесятичное представлениеиз int64
Тип срез.true
Войти,false
Указывает, что вход не выполнен.Мы можем отобразить статус регистрации месяца на основе логического среза из элемента существующего клиента, например Календарь регистрации。
В этой статье подробно описано, как использовать Redis Bitmap
тип Достичь высокой эффективности из Вход статистическая функция пользователя. Содержание включает в себя Redis Bitmap
Краткое введение в типы данных и сценарии их применения, а также Go
Языковая программа просто реализована Вход пользователя、Запрос Вход пользователясостояние и Статистика совокупного количества дней заезда в этом году а также Статистика заездов за текущий месяц из функции.
Хотя Redis bitmap
Тип данных существуетstatisticsВход Пользователь имеет значительные преимущества с точки зрения ситуации, что в основном отражает следующие два момента:
Однако,Redis Bitmap
Типы данных также имеют свои ограничения. Например, используйте Bitmap
При хранении данных,Можно сохранить только одно состояние. Если вам нужно сохранить дополнительную информацию о конкретном времени заезда или другую соответствующую информацию,Bitmap
не применяется.
В общем,Redis Bitmap
Он очень подходит для реализации эффективной функции статистики регистрации, но при разработке системы необходимо взвесить ее преимущества и недостатки в соответствии с конкретными потребностями.