Збільшення швидкодії Redis-у через розділення на домени
Redis дуже простий і потужний інструмент, але хоч і має велику швидкодію та все ж однопотоковий.
Та якщо для кожного домену виділити окремий Redis то можна збільшити швидкодію, про це і буде дана стаття, про мікробенчмарки.
Передісторія
Майже в кожній компанії (6 із 7), де працював, використовувався Redis, а коли працював в LeBoutique то для кожного домену використовувався свій окремий Redis, перший для кешування HTML сторінок (@content), другий для збереження онлайну користувачів (@online), і третій для збереження лічильників переглядів продуктів (@views).
Порівняння швидкодії
Підготуємо три Redis-а та напишемо бенчмарки з використанням команди increment.
import (
"sync/atomic"
"testing"
"github.com/go-redis/redis"
"github.com/stretchr/testify/require"
)
var (
keys = []string{
"1",
"2",
"3",
"4",
"5",
}
keyCount = uint32(len(keys))
)
// https://github.com/go-redis/redis#quickstart
func Client(addr string) (*redis.Client, error) {
client := redis.NewClient(&redis.Options{
Addr: addr, // "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
err := client.Ping().Err()
if err != nil {
return nil, err
}
return client, err
}
func BenchmarkRedis1Increment(b *testing.B) {
const (
addr1 = "redis1:6379"
)
benchmarkIncrement(b, addr1, addr1, addr1)
}
func BenchmarkRedisAllIncrement(b *testing.B) {
const (
addr1 = "redis1:6379"
addr2 = "redis2:6379"
addr3 = "redis3:6379"
)
benchmarkIncrement(b, addr1, addr2, addr3)
}
func benchmarkIncrement(b *testing.B, addrs ...string) {
b.Helper()
var (
clientCount = uint32(len(addrs))
clients = make([]*redis.Client, clientCount)
)
for i, addr := range addrs {
var client, err = Client(addr)
require.NoError(b, err)
defer client.Close()
var flushAllErr = client.FlushAll().Err()
require.NoError(b, flushAllErr)
clients[i] = client
}
var counter = uint32(0)
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
var index = atomic.AddUint32(&counter, 1)
var client = clients[index%clientCount]
var err = client.Incr(keys[index%keyCount]).Err()
require.NoError(b, err)
}
})
}
В BenchmarkRedis1Increment використовується тільки 1 Redis, а в BenchmarkRedisAllIncrement вже 3 різні Redis-и.
Запустимо тести і порівнюємо швидкодію.
go test ./... -v -bench=. -benchmem -count=10 > bench.txt
benchstat bench.txt
name time/op Redis1Increment 8.36µs ± 2% RedisAllIncrement 5.54µs ± 2%
На такому простому прикладі швидкодія збільшилась на 33%.
Багатопотокова альтернатива KeyDB
KeyDB is a high performance fork of Redis with a focus on multithreading.
Заради цікавості дописав тести для KeyDB, ось повна картина:
name time/op Redis1Increment 9.30µs ± 1% RedisAllIncrement 6.68µs ± 8% Keydb1Increment 10.8µs ± 1% KeydbAllIncrement 8.05µs ± 1% Dragonflydb1Increment 12.3µs ± 3% DragonflydbAllIncrement 14.6µs ± 3%
Ще одна альтернатива Dragonfly
Dragonfly started as an experiment to see how an in-memory datastore could look like if it was designed in 2022Після новини на DOU додав тести для Dragonfly в репозиторій.
Висновки
Звісно на інших операціях з Redis збільшення швидкодії буде відрізнятись, але воно буде якщо розділяти на домени.
Мікробенчмарк доступний в репозиторії, можете використовувати як заготовку для ваших досліджень.
4 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарівВ статті це не вказано, але, як я розумію, redis процеси запущені на тій самій машині, що і бенчмарк. Цікаво, чи це відповідає вашому продакшн середовищу. Якщо ні, то мережеві виклики можуть суттєво зменшити відносний виграш по latency.
В основному старались щоб Redis знаходився на одній стійці з сервісом який його використовує, в нас в продакшені є N серверів де на кожному мікросервіс з власним Redis-ом.
А в одному з попередніх проектів був моноліт і 3 Redis-а, кожен для свого домену і все на одному потужному сервері
Погоджуюсь що мережеві виклики значно зменшать відносний виграш для простих операцій, але для важких операцій розділення на домени буде актуальним
В будь-якому разі навіть прочитавши статтю краще протестувати рішення для конкретної задачі ніж вірити
1. Чем это отличается от шардирования?
2. Получится использовать KeyDB для локов?
Механізм однаковий
Distributed locks with Redis
Distributed locks with KeyDB
Статті однакові або дуже схожі