JSON을 나타내는 Redis 문자열 vs Redis 해시: 효율성?
JSON payload를 redis에 저장하고 싶다.여기에는 두 가지 방법이 있습니다.
간단한 문자열 키와 값을 사용하는 것.
의 JSON ): "", ": payload(JSON blob "(100~200KB)"SET user:1 payload
해시 사용
HSET user:1 username "someone"
HSET user:1 location "NY"
HSET user:1 bio "STRING WITH OVER 100 lines"
해시를 사용하면 값 길이를 예측할 수 없습니다.위의 예시와 같이 모두 짧은 것은 아닙니다.
어느 쪽이 메모리 효율이 높습니까?문자열 키와 값을 사용하시겠습니까, 아니면 해시를 사용하시겠습니까?
이 기사에서는 많은 정보를 얻을 수 있습니다.http://redis.io/topics/memory-optimization
개체 배열을 Redis에 저장하는 방법은 여러 가지가 있습니다(스포일러:대부분의 사용 사례에서 옵션 1을 선호합니다.)
개체 전체를 단일 키에 JSON 인코딩 문자열로 저장하고 세트(또는 더 적절한 경우 목록)를 사용하여 모든 개체를 추적합니다.예를 들어 다음과 같습니다.
INCR id:users SET user:{id} '{"name":"Fred","age":25}' SADD users {id}
일반적으로, 이것은 대부분의 경우에 가장 좋은 방법일 것입니다.객체에 필드가 많고 객체가 다른 객체와 중첩되지 않고 한 번에 필드의 작은 부분 집합에만 액세스하는 경향이 있는 경우 옵션 2를 사용하는 것이 좋습니다.
장점: "좋은 관행"으로 간주됩니다.각 오브젝트는 완전한 Redis 키입니다.JSON 해석은 특히 이 오브젝트의 여러 필드에 동시에 액세스해야 할 때 빠릅니다.단점: 단일 필드에만 액세스해야 하는 경우 속도가 느립니다.
각 개체의 속성을 Redis 해시에 저장합니다.
INCR id:users HMSET user:{id} name "Fred" age 25 SADD users {id}
장점: "좋은 관행"으로 간주됩니다.각 오브젝트는 완전한 Redis 키입니다.JSON 문자열을 해석할 필요가 없습니다.단점: 오브젝트의 모든 필드 또는 대부분의 필드에 액세스해야 할 경우 속도가 느려질 수 있습니다.또한 중첩된 개체(개체 내의 개체)는 쉽게 저장할 수 없습니다.
각 개체를 Redis 해시에 JSON 문자열로 저장합니다.
INCR id:users HMSET users {id} '{"name":"Fred","age":25}'
이를 통해 약간의 통합이 가능하며 많은 키 대신 2개의 키만 사용할 수 있습니다.분명한 단점은 각 사용자 오브젝트에 TTL(및 기타 정보)을 설정할 수 없다는 것입니다.이는 Redis 해시 내의 필드일 뿐 완전한 Redis 키가 아니기 때문입니다.
이점: JSON 구문 분석 속도가 빠릅니다. 특히 이 개체의 여러 필드에 동시에 액세스해야 할 경우 더욱 그렇습니다.기본 키 네임스페이스의 "오염"이 적습니다.단점:오브젝트가 많은 경우의 메모리 사용량은 #1과 거의 동일합니다.단일 필드에만 액세스하면 되는 #2보다 느립니다.아마도 "좋은 관행"으로 여겨지지 않을 것이다.
각 개체의 각 속성을 전용 키에 저장합니다.
INCR id:users SET user:{id}:name "Fred" SET user:{id}:age 25 SADD users {id}
위 기사에 따르면 이 옵션은 거의 선호되지 않습니다(오브젝트의 속성이 특정 TTL 또는 다른 것을 가질 필요가 없는 경우).
장점:오브젝트 속성은 완전한 Redis 키이므로 앱에 과도한 영향을 주지 않을 수 있습니다.단점: 속도가 느리고 메모리를 더 많이 사용하며 "베스트 프랙티스"로 간주되지 않습니다.메인 키 네임스페이스가 많이 오염됩니다.
전체적인 개요
옵션 4는 일반적으로 권장되지 않습니다.옵션 1과 2는 매우 비슷하며 둘 다 매우 일반적입니다.옵션 1(일반적으로 말하면)을 사용하면 더 복잡한 오브젝트(복수의 중첩 레이어 등)를 저장할 수 있기 때문에 선호합니다.옵션 3은 메인 키 네임스페이스를 오염시키지 않을 때 사용합니다(즉, 데이터베이스에 많은 키가 있는 것을 원하지 않으며 TTL, 키 샤딩 등의 문제는 신경 쓰지 않습니다).
만약 제가 여기서 틀린 부분이 있다면, 댓글을 달아 다운 투표 전에 답변을 수정하도록 허락해 주세요.감사합니다! :)
데이터에 액세스하는 방법에 따라 달라집니다.
옵션 1로 이동:
- 대부분의 액세스에서 대부분의 필드를 사용하는 경우.
- 가능한 키에 차이가 있는 경우
옵션 2로 이동:
- 대부분의 액세스에서 단일 필드만 사용하는 경우.
- 사용 가능한 필드를 항상 알고 있는 경우
추신: 일반적으로 대부분의 사용 사례에서 필요한 쿼리가 적은 옵션을 선택하십시오.
특정 답변에 대한 몇 가지 추가 사항:
우선 Redis 해시를 효율적으로 사용하려면 키 카운트의 최대 수와 값의 최대 크기를 알아야 합니다.그렇지 않으면 hash-max-ziplist-value 또는 hash-max-ziplist-entries가 분리되면 Redis는 이를 후드 아래에서 실질적으로 일반적인 키/값 쌍으로 변환합니다.( 「hash-max-ziplist-value, hash-max-ziplist-entries」를 참조해 주세요).또, Redis내의 통상의 키/값의 각 페어가 페어 마다 +90 바이트를 사용하고 있기 때문에, 해시 옵션의 후드아래에서의 브레이크는 정말로 불량입니다.
즉, 옵션2에서 시작하여 실수로 max-hash-ziplist-value에서 벗어나면 내부 사용자 모델에 있는 각 Attribute마다 +90바이트가 됩니다(실제로 +90이 아니라 +70의 콘솔 출력을 참조해 주세요).
# you need me-redis and awesome-print gems to run exact code
redis = Redis.include(MeRedis).configure( hash_max_ziplist_value: 64, hash_max_ziplist_entries: 512 ).new
=> #<Redis client v4.0.1 for redis://127.0.0.1:6379/0>
> redis.flushdb
=> "OK"
> ap redis.info(:memory)
{
"used_memory" => "529512",
**"used_memory_human" => "517.10K"**,
....
}
=> nil
# me_set( 't:i' ... ) same as hset( 't:i/512', i % 512 ... )
# txt is some english fictionary book around 56K length,
# so we just take some random 63-symbols string from it
> redis.pipelined{ 10000.times{ |i| redis.me_set( "t:#{i}", txt[rand(50000), 63] ) } }; :done
=> :done
> ap redis.info(:memory)
{
"used_memory" => "1251944",
**"used_memory_human" => "1.19M"**, # ~ 72b per key/value
.....
}
> redis.flushdb
=> "OK"
# setting **only one value** +1 byte per hash of 512 values equal to set them all +1 byte
> redis.pipelined{ 10000.times{ |i| redis.me_set( "t:#{i}", txt[rand(50000), i % 512 == 0 ? 65 : 63] ) } }; :done
> ap redis.info(:memory)
{
"used_memory" => "1876064",
"used_memory_human" => "1.79M", # ~ 134 bytes per pair
....
}
redis.pipelined{ 10000.times{ |i| redis.set( "t:#{i}", txt[rand(50000), 65] ) } };
ap redis.info(:memory)
{
"used_memory" => "2262312",
"used_memory_human" => "2.16M", #~155 byte per pair i.e. +90 bytes
....
}
The Hippo 답변의 경우 옵션1에 대한 코멘트는 오해의 소지가 있습니다.
모든 필드 또는 여러 get/set 조작이 필요한 경우 hgetall/hmset/hmget을 rescue로 이동합니다.
BMiner의 답변.
세 번째 옵션은 실제로 매우 재미있습니다.max(id) < has-max-ziplist-value > 데이터 세트의 경우 이 솔루션은 O(N) 복잡성이 있습니다.왜냐하면 Reddis는 작은 해시를 길이/키/값 객체의 어레이 같은 컨테이너로 저장하기 때문입니다.
그러나 해시에는 몇 개의 필드만 포함되어 있는 경우가 많습니다.해시가 작을 경우 대신 O(N) 데이터 구조(길이 프리픽스 키 값 쌍이 있는 선형 배열)로 인코딩할 수 있습니다.N이 작을 때만 이 작업을 수행하므로 HGET 및 HSET 명령의 상각 시간은 O(1)입니다.해시에 포함되는 요소의 수가 너무 많아지면 해시는 실제 해시 테이블로 변환됩니다.
그러나 해시-max-ziplist-entries는 매우 빠르게 깨지고 솔루션 넘버1이 됩니다
두 번째 옵션은 대부분의 경우 다음 질문에서 언급되었듯이 네 번째 솔루션으로 이동합니다.
해시를 사용하면 값 길이를 예측할 수 없습니다.위의 예시와 같이 모두 짧은 것은 아닙니다.
그리고 이미 말씀하셨듯이, 네 번째 솔루션이 각 속성당 가장 비싼 +70바이트입니다.
이러한 데이터 세트를 최적화하는 방법을 제안합니다.
두 가지 옵션이 있습니다.
일부 사용자 속성의 최대 크기를 보장할 수 없는 경우 첫 번째 솔루션을 선택하고 메모리 문제가 중요한 경우 사용자 json을 압축한 후 redis로 저장하십시오.
모든 Atribute의 최대 사이즈를 강제할 수 있는 경우.그런 다음 hash-max-ziplist-dis/value를 설정하여 사용자 표현당 하나의 해시 또는 Redis 가이드의 이 토픽에서 해시 메모리 최적화로 사용할 수 있습니다.https://redis.io/topics/memory-optimization 및 store user as json string.어느 쪽이든 긴 사용자 속성을 압축할 수 있습니다.
생산환경에서도 비슷한 문제가 있었습니다만, payload가 임계값 KB를 넘으면 gzipping 하는 아이디어가 떠올랐습니다.
여기 Redis 클라이언트 lib 전용 레포만 있습니다.
기본 개념은 사이즈가 임계값보다 큰 경우 payload를 검출하여 gzip하고 base-64로 압축된 문자열을 redis에 일반 문자열로 유지하는 것입니다.on retrieve는 문자열이 유효한 base-64 문자열인지 여부를 검출하고 유효한 경우 압축을 해제합니다.
압축 및 압축 해제 전체가 투과적이며 네트워크 트래픽의 50% 가까이를 얻을 수 있습니다.
압축 벤치마크 결과
BenchmarkDotNet=v0.12.1, OS=macOS 11.3 (20E232) [Darwin 20.4.0]
Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=5.0.201
[Host] : .NET Core 3.1.13 (CoreCLR 4.700.21.11102, CoreFX 4.700.21.11602), X64 RyuJIT DEBUG
방법 | 의미하다 | 에러 | 표준 개발 | 0세대 | 제1세대 | 제2세대 | 할당필 |
---|---|---|---|---|---|---|---|
Compression Benchmark 사용 | 668.2 밀리초 | 13.34 밀리초 | 27.24 밀리초 | - | - | - | 4.88 MB |
Compression Benchmark 미포함 | 1,387.1 밀리초 | 26.92 밀리초 | 37.74 밀리초 | - | - | - | 2.39 MB |
JSON을 Redis에 저장하려면 Redis JSON 모듈을 사용합니다.
다음과 같은 이점을 얻을 수 있습니다.
- JSON 표준 완전 지원
- 문서 내부의 요소를 선택/업데이트하기 위한 JSONPath 구문
- 트리 구조의 이진 데이터로 저장된 문서를 통해 하위 요소에 빠르게 액세스할 수 있습니다.
- 모든 JSON 값 유형에 대해 유형화된 원자 연산
https://redis.io/docs/stack/json/
https://developer.redis.com/howtos/redisjson/getting-started/
https://redis.com/blog/redisjson-public-preview-performance-benchmarking/
json 모듈을 사용할 수 있습니다.https://redis.io/docs/stack/json/ 이것은 완전히 지원되며 json을 redis의 데이터 구조로 사용할 수 있습니다.일부 언어용 Redis Object Mappers도 있습니다.https://redis.io/docs/stack/get-started/tutorials/
언급URL : https://stackoverflow.com/questions/16375188/redis-strings-vs-redis-hashes-to-represent-json-efficiency
'programing' 카테고리의 다른 글
react-select drowdown에서 zIndex를 변경하는 방법 (0) | 2023.03.14 |
---|---|
스프링 부트 웹 응용 프로그램에서 JSP 파일이 렌더링되지 않음 (0) | 2023.03.14 |
application.properties를 사용하여 봄에 CSRF를 비활성화하려면 어떻게 해야 합니까? (0) | 2023.03.14 |
WordPress에서 wp_get_nav_menu_items를 사용하여 커스텀 메뉴/서브메뉴 시스템을 생성하는 방법은 무엇입니까? (0) | 2023.03.14 |
스프링 부트 기본 로그 위치 (0) | 2023.03.14 |