Một số bổ sung cho một bộ câu trả lời nhất định:
Trước hết, nếu bạn sử dụng Redis hash một cách hiệu quả, bạn phải biết một khóa đếm số lượng tối đa và giá trị kích thước tối đa - nếu không, nếu chúng phá vỡ hàm băm-max-ziplist-value hoặc hash-max-ziplist-Redis sẽ chuyển đổi nó thành thực tế cặp khóa / giá trị thông thường dưới mui xe. (xem băm-max-ziplist-value, hash-max-ziplist-entry) Và phá vỡ một mui xe từ một tùy chọn băm là THỰC SỰ BAD, bởi vì mỗi cặp khóa / giá trị thông thường trong Redis sử dụng +90 byte mỗi cặp.
Điều đó có nghĩa là nếu bạn bắt đầu với tùy chọn hai và vô tình thoát ra khỏi giá trị max-hash-ziplist-value, bạn sẽ nhận được +90 byte mỗi MACHI ATTRIBUTE mà bạn có trong mô hình người dùng! (thực tế không phải là +90 mà là +70 xem đầu ra giao diện điều khiển bên dưới)
# 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
....
}
Đối với câu trả lời của TheHippo, các nhận xét về Tùy chọn một là sai lệch:
hgetall / hmset / hmget để giải cứu nếu bạn cần tất cả các trường hoặc nhiều thao tác get / set.
Đối với câu trả lời BMiner.
Tùy chọn thứ ba thực sự rất thú vị, đối với tập dữ liệu có max (id) <has-max-ziplist-value giải pháp này có độ phức tạp O (N), bởi vì, thật bất ngờ, Reddis lưu trữ các giá trị băm nhỏ dưới dạng bộ chứa độ dài / khóa / giá trị giống như mảng các đối tượng!
Nhưng nhiều lần băm chỉ chứa một vài trường. Thay vào đó, khi băm nhỏ, chúng ta có thể chỉ mã hóa chúng trong cấu trúc dữ liệu O (N), giống như một mảng tuyến tính với các cặp giá trị khóa có tiền tố dài. Vì chúng ta chỉ làm điều này khi N nhỏ, thời gian khấu hao cho các lệnh HGET và HSET vẫn là O (1): hàm băm sẽ được chuyển đổi thành bảng băm thực sự ngay khi số phần tử mà nó chứa sẽ tăng quá nhiều
Nhưng bạn không nên lo lắng, bạn sẽ phá vỡ các mục nhập hash-max-ziplist rất nhanh và bạn sẽ thực sự ở giải pháp số 1.
Tùy chọn thứ hai rất có thể sẽ đi đến giải pháp thứ tư trong một mui xe vì như câu hỏi nêu:
Hãy nhớ rằng nếu tôi sử dụng hàm băm, độ dài giá trị không thể dự đoán được. Chúng không phải là tất cả ngắn như ví dụ sinh học ở trên.
Và như bạn đã nói: giải pháp thứ tư là +70 byte đắt nhất cho mỗi thuộc tính chắc chắn.
Đề nghị của tôi làm thế nào để tối ưu hóa tập dữ liệu đó:
Bạn có hai lựa chọn:
Nếu bạn không thể đảm bảo kích thước tối đa của một số thuộc tính người dùng so với giải pháp đầu tiên và nếu vấn đề bộ nhớ là quan trọng hơn nén json người dùng trước khi lưu trữ trong redis.
Nếu bạn có thể buộc kích thước tối đa của tất cả các thuộc tính. Hơn bạn có thể đặt băm-max-ziplist-entry / value và sử dụng băm dưới dạng một hàm băm cho mỗi đại diện người dùng HOẶC làm tối ưu hóa bộ nhớ băm từ chủ đề này của hướng dẫn Redis: https://redis.io/topics/memory-optimization và lưu trữ người dùng như chuỗi json. Dù bằng cách nào bạn cũng có thể nén các thuộc tính người dùng dài.