Хешовете на Redis са (достатъчно интуитивно!) хешове, които съпоставят имената на низове със стойности на низове. По същество те са наречени контейнери с уникални полета и техните стойности. Те са идеалният начин да представите обект като структура от данни на Redis. Както се очаква, те осигуряват постоянно време основни операции като get, set, exists и т.н. Предоставени са и куп разширени операции. Пълният списък с хеш команди е тук.
Нека го разгледаме от redis-cli .
# hmset key field value [field value ...] : Insert elements in a hash. O(N), N is # of field being set 127.0.0.1:6379> hmset std:101 name "John Smith" dob "01-01-2000" gender M active 0 cgpa 2.9 OK # hgetall key : key all keys and values in the hash. O(N), N is size of hash 127.0.0.1:6379> hgetall std:101 1) "name" 2) "John Smith" 3) "dob" 4) "01-01-2000" 5) "gender" 6) "M" 7) "active" 8) "0" 9) "cgpa" 10) "2.9" 127.0.0.1:6379> hmset std:102 name "Jane" name "Ann" OK # If duplicates are found, only the last set is valid 127.0.0.1:6379> hgetall std:102 1) "name" 2) "Ann" # hexists key field: does field exist in the hash with key. O(1) 127.0.0.1:6379> hexists std:102 cgpa (integer) 0 # hincrby key field increment: Increment the integer field by increment. O(1) 127.0.0.1:6379> hincrby std:101 active 1 (integer) 1 # hget key field : the value for field in the hash stored at key. O(1) 127.0.0.1:6379> hget std:101 active 1) "1" # If field doesn't exist, hincrby sets it to 0 and then applies increment 127.0.0.1:6379> hincrby std:102 active 2 (integer) 2 # hmget key field [field ...]: the values of the fields requested for the hash with key. O(N), N is # of keys requested 127.0.0.1:6379> hmget std:102 active 1) "2" # hincrbyfloat key field increment: Increment the float value in field by increment. O(1) 127.0.0.1:6379> HINCRBYFLOAT std:101 cgpa 1.0 "3.9" # HSETNX key field value: set field to value if not alread set. O(1) 127.0.0.1:6379> hsetnx std:102 cgpa 4.0 (integer) 1 127.0.0.1:6379> hget std:102 cgpa "4.0" # hlen key: number of fields in the hash. O(1) 127.0.0.1:6379> hlen std:101 (integer) 5 # hkeys key : all fields in the hash. O(N), N is size of hash 127.0.0.1:6379> hkeys std:101 1) "name" 2) "dob" 3) "gender" 4) "active" 5) "cgpa"
Както очаквахме от нашия хостинг за Redis™* като сървър за структура на данни, виждаме, че Redis предоставя доста полезни и разширени операции с хешове.
Вътрешни елементи
Подобно на наборите на Redis, хешовете на Redis също се реализират като речници. Речниците в Redis се изпълняват като хеш таблици, които използват хеш функцията MurmurHash2 и се увеличават чрез инкрементално преоразмеряване. Хеш колизиите се обработват чрез верижно свързване. Повече подробности можете да намерите в реализацията на Redis на речника на dict.c.
Както при наборите, има оптимизация на съхранението, направена за по-малки хешове. Тази структура от данни се нарича ziplist (хешовете са оптимизирани с помощта на различна структура от данни, наречена zipmap преди Redis 2.6) в реализацията на Redis. По същество това е специално кодиран двусвързан списък, който е оптимизиран за спестяване на памет. Данните, както и указателите се съхраняват на линия. Ziplist се използва и за оптимизиране на съхранението на по-малки сортирани набори и списъци. Хеш, когато е изравнен в такъв списък, изглежда нещо като [ключ1, стойност1, ключ2, стойност2, ...]. Как това е по-ефективно от обикновените ключове? Хешовете с няколко ключа могат да бъдат опаковани умело в тази структура като линеен масив (т.е. ziplist), като същевременно се гарантира амортизирана O(1) производителност за получаване и задаване. Очевидно това не може да продължи, тъй като хеш полетата се увеличават. Тъй като хешът нараства, той се преобразува в стандартната речникова структура, за да се поддържа O(1) производителност и спестяването на пространство се губи. Конфигурационните параметри на Redis, контролиращи тази трансформация, са:
- list-max-ziplist-entries по подразбиране (512):Промяна на стандартно представяне, ако хешът нарасне по-голям от това ограничение.
- list-max-ziplist-value по подразбиране (64):Промяна на стандартно представяне, ако най-големият елемент в хеша стане по-голям от това ограничение.
Повече подробности могат да бъдат разбрани от кода и коментарите в реализацията, намиращи се тук. Спестяванията на памет от използването на тази специална оптимизация са значителни. Ще говорим за повече подробности в следващия раздел.
Оптимизация на паметта
Една от добре познатите препоръки за спестяване на памет, докато използвате Redis, е да използвате хешове вместо обикновени низове. Това е важен случай на използване за използване на силата на хешовете на Redis в приложения от реалния свят. От официалната документация на Redis за оптимизирането на паметта:
Използвайте хешове, когато е възможно
Малките хешове са кодирани в много малко пространство, така че трябва да опитате да представите данните си с помощта на хешове всеки път, когато е възможно. Например, ако имате обекти, представляващи потребители в уеб приложение, вместо да използвате различни ключове за име, фамилия, имейл, парола, използвайте един хеш с всички задължителни полета.
След това тази публикация предлага един начин за картографиране на диапазон от обекти в набор от хешове, за да се възползвате от спестяванията на паметта. Instagram, в много популярна публикация в блог, описва използването на подобна техника, която им помогна да постигнат порядък на потенциални спестявания. Друг блог, който се опитва да измери ползите от оптимизацията е този.
Приложения
- Хешовете на Redis са естествено подходящи за съхранение на обекти:сесии, потребители, посетители и т.н. Това го прави една от ключовите структури от данни, предоставени от Redis.
- В своята оптимизирана за паметта форма, тя е отличен избор за кеширане на големи количества данни.
Съхранение на адреси на обекти
Тъй като оптимизацията на паметта е важен случай на използване на хешовете, нека обсъдим пример, подобен на внедряването в Instagram, за да покажем как да използвате функциите за спестяване на памет на хешовете на Redis. Да кажем, че имаме огромно внедряване на хранилище с адресиране на съдържание (CAS) със стотици милиони съхранявани обекти. Местоположението на всеки обект е хеш низ. Възнамеряваме да разработим система за търсене, за да разберем местоположението на обекта, като се има предвид неговият идентификатор. Типичният начин да направите това в Redis е да използвате низ.
set object:14590860 "007f80f0a62408..."
set object:11678 "009f80abcd0a60..."
...
Този подход работи добре. Въпреки това, тъй като броят на обектите, които имаме, е огромен, в крайна сметка ще се нуждаем от много памет за Redis. Искаме да се справим по-добре. Нека вземем оптимизирания за паметта хеш подход към този проблем. Ще трябва да изберем правилните стойности за list-max-ziplist-entries и list-max-ziplist-value . Правилната стойност за list-max-ziplist-value е каквато може да бъде максималната дължина на хеш низа на адреса за съхранение. Стойността на list-max-ziplist-entries трябва да се поддържа достатъчно нисък и ще зависи от броя на общите хеш кофи, които искаме да създадем. Най-добре ще бъде установено емпирично. За напр. за 100 милиона обекта бихме могли да изберем да използваме 100k хешове. Максималните записи в този случай ще бъдат 100m / 100k =1000. Логиката на приложението да реши в кой хеш влиза адресът за съхранение на обект може да бъде:разделете идентификатора на обекта на 100k и изхвърлете остатъка. Така обект ID 14590860 ще влезе в хеш (14590860/100k) =145 т.е.
hset object:145 14590860 "007f80f0a62408..."
hget object:145 14590860
> "007f80f0a62408..."
Тази реализация не само ще бъде много по-лека за паметта, но също така трябва да осигури добро местоположение на кеша.
Ето и другите ни публикации в поредицата за структура на данните на Redis.
- Набори на Redis
- Растрови изображения на Redis
- Сортирани набори на Redis