Использование Redis для кэширования может сэкономить вам много времени, поскольку вам не нужно разрешать ваш объект или извлекать данные из вашей базы данных. Но если вы хотите работать с коллекциями, вы можете столкнуться с дилеммой: хранить или не хранить их в базе данных Redis.
Если вы храните всю свою коллекцию в одной строке в кеше, каждый раз, когда вы извлекаете один или несколько элементов (например, данные об одном сотруднике), вся ваша коллекция должна быть сериализована, чтобы затем фильтровать нужные вам объекты. Если у вас большая коллекция (более 10 тысяч элементов), это ухудшит производительность вашего приложения, особенно если вы храните сложную структуру данных.
Использование Redis Hash может значительно ускорить эти запросы к вашей коллекции.
Итак, для этого давайте рассмотрим способ получить максимальную отдачу от вашей базы данных Redis с помощью Hash!
Что такое хэши Redis?
Хэши Redis — это типы записей, структурированные как наборы сопоставлений полей и значений таким образом, что они не требуют много места.
Основное преимущество использования Redis Hash для ваших коллекций заключается в том, что вы можете напрямую обращаться к нужному вам реестру, просто используя имя поля для доступа к нему. Если вам не нужно визуализировать всю коллекцию, ее можно получить гораздо быстрее.
Вы можете работать со строками как для ключа, так и для значения, поэтому становится легко обрабатывать простые структуры или даже целые объекты JSON в вашем хеше.
Использование хэша Redis
Вот ключевые команды, которые вы можете использовать для управления своим хэшем в Redis:
HSET
Устанавливает значение одного или нескольких полей в вашем хэше
HSET key field value [field value ...]
HGET
Возвращает значение в определенное поле в вашем хэше
HGET key field
HMGET
Возвращает значения, связанные с указанным fields
в хэше, хранящемся в key
.
HMGET key field [field ...]
HGETALL
Возвращает все поля и значения хеша, хранящиеся в key
.
HGETALL key
Хранение вашей коллекции в Redis Hash
Ниже вы можете увидеть пример того, как вы можете реализовать свою коллекцию в Redis Hash, используя C# и пакет StackExchange.Redis NuGet:
using Newtonsoft.Json; using StackExchange.Redis; namespace RedisTest.Redis { public class RedisHash { protected IDatabase RedisDatabase { get; } protected ConnectionMultiplexer Connection { get; } public RedisHash(string redisServer, string password = "", int redisDatabaseId = 0) { var config = new ConfigurationOptions { EndPoints = { redisServer }, Password = password ?? "", DefaultDatabase = redisDatabaseId, AllowAdmin = true, SyncTimeout = 20000, }; Connection = ConnectionMultiplexer.Connect(config); RedisDatabase = Connection.GetDatabase(redisDatabaseId); } public bool SetCollection<T>(string key, IEnumerable<(string field, T value)> cacheObject, TimeSpan expiryTime) { RedisDatabase.HashSet( key, cacheObject .Select(itm => new HashEntry(itm.field, JsonConvert.SerializeObject(itm.value))) .ToArray()); return RedisDatabase.KeyExpire(key, expiryTime); } public IEnumerable<T> GetCollection<T>(string key) { var result = RedisDatabase.HashGetAll(key); if (result.Length > 0) { var items = result.Where(e => e.Value.HasValue) .Select(e => JsonConvert.DeserializeObject<T>(e.Value)) .ToList(); return items; } return Array.Empty<T>(); ; } public IEnumerable<T> GetItemsFromCollection<T>(string key, params string[] fields) { var hashResult = RedisDatabase.HashGet( key, fields.Select(key => (RedisValue)key).ToArray()); var items = hashResult.Where(e => e.HasValue) .Select(e => JsonConvert.DeserializeObject<T>(e)) .ToList(); return items; } } }
У нас есть 3 метода в этом классе:
- SetCollection
- ПолучитьКоллекцию
- GetItemsFromCollection
И вот как вы можете использовать эти методы:
public class Employee { public int Id { get; init; } public string Name { get; init; } public string UserName { get; init; } } using RedisTest.Redis; const string cacheKey = "myRedisHashKey"; //Creates a new redis cache instance var cacheInstance = new RedisHash("127.0.0.1"); //Create the employee instances var employeeJohn = new Employee { Id = 1, Name = "John White", UserName = "john.white" }; var employeeJack = new Employee { Id = 2, Name = "Jack Black", UserName = "jack.black" }; var employeeLucas = new Employee { Id = 3, Name = "Lucas Brown", UserName = "lucas.brown" }; //Sets the employees in the collection cacheInstance.SetCollection( cacheKey, TimeSpan.FromMinutes(5), new List<(string, Employee)> { (employeeJohn.UserName, employeeJohn), (employeeJack.UserName, employeeJack), (employeeLucas.UserName, employeeLucas) }); //Gets all the employees from Redis var allEmployees = cacheInstance.GetCollection<Employee>(cacheKey); //Gets only Jack and John from Redis var jackAndJohn = cacheInstance.GetItemsFromCollection<Employee>(cacheKey, "jack.black", "john.white");
Производительность
Ниже вы можете сравнить использование Redis String Set и Redis Hash при работе с коллекцией из 20 тыс. элементов со сложной структурой данных:
PS.: Вы также можете выполнить тесты самостоятельно, используя код в GitHub.
RedisTest.Redis.RedisHash SetCollection(20000): avg: 561 ms //Sets all the 20k items in the collection GetCollection(20000): avg: 1039 ms //Retrieves all the 20k items in the collection GetItemsFromCollection(1000): avg: 23 ms //Retrieves 1k items in the collection RedisTest.Redis.RedisCache Set(20000): avg: 752 ms //Sets all the 20k items in the collection Get(20000): avg: 870 ms //Retrieves all the 20k items in the collection GetItems(1000): avg: 473 ms //Retrieves 1k items in the collection
Как видно из приведенных выше результатов, получение и/или установка всех элементов с использованием двух подходов может иметь одинаковую производительность, но получение только выбранных имеет огромную разницу.
Для предложенного примера это в 20 раз быстрее при использовании Redis Hash. Эта разница может стать еще больше с коллекциями, содержащими более 100 000 элементов или более сложной структурой.
Заключение
Кэши должны улучшить производительность наших приложений. Но, в зависимости от использования, это может создать в нем новое узкое место. Использование Redis Hash может помочь устранить некоторые из тех пробелов в производительности, которые у вас есть.
У вас есть другие идеи о том, как использовать Redis Hash?
Поделитесь со мной в комментариях ниже.
Чтобы узнать об этом больше…
https://redis.io/docs/manual/data-types/#hashes
https://redis.io/ docs/data-types/hashes/
https://github.com/StackExchange/StackExchange.Redis
https://stackexchange.github.io/StackExchange.Redis/
Чтобы проверить код, вы можете получить его на моем GitHub.
https://github.com/danilobsi/Redis-Collection-Store/tree/main/RedisTest