This is an automated email from the ASF dual-hosted git repository.
twice pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new c124922ac perf(hash): use MultiGet to reduce RocksDB calls in HMSET
(#3327)
c124922ac is described below
commit c124922accd6cb8af3cb99d10901d2909a2c9268
Author: sryan yuan <[email protected]>
AuthorDate: Wed Jan 7 18:59:34 2026 +0800
perf(hash): use MultiGet to reduce RocksDB calls in HMSET (#3327)
HMSET previously compared existing values by issuing a separate RocksDB
Get
for each field. When the number of fields is large, this results in
multiple
Get calls and extra overhead.
This change replaces multiple Get calls with a single MultiGet call,
batching
the reads and improving performance for HMSET with many fields.
---------
Co-authored-by: yxj25245 <[email protected]>
Co-authored-by: hulk <[email protected]>
Co-authored-by: Twice <[email protected]>
---
src/types/redis_hash.cc | 43 +++++++++++++++++++++++++++++++++----------
1 file changed, 33 insertions(+), 10 deletions(-)
diff --git a/src/types/redis_hash.cc b/src/types/redis_hash.cc
index 75159c731..cbb5fa0ef 100644
--- a/src/types/redis_hash.cc
+++ b/src/types/redis_hash.cc
@@ -257,29 +257,52 @@ rocksdb::Status Hash::MSet(engine::Context &ctx, const
Slice &user_key, const st
s = batch->PutLogData(log_data.Encode());
if (!s.ok()) return s;
std::unordered_set<std::string_view> field_set;
+
+ std::vector<rocksdb::Slice> keys;
+ std::vector<std::string> keys_encoded;
+ std::vector<std::string_view> values;
+ keys.reserve(field_values.size());
+ values.reserve(field_values.size());
for (auto it = field_values.rbegin(); it != field_values.rend(); it++) {
if (!field_set.insert(it->field).second) {
continue;
}
- bool exists = false;
- std::string sub_key = InternalKey(ns_key, it->field, metadata.version,
storage_->IsSlotIdEncoded()).Encode();
+ keys_encoded.push_back(InternalKey(ns_key, it->field, metadata.version,
storage_->IsSlotIdEncoded()).Encode());
+ keys.emplace_back(keys_encoded.back());
+ values.emplace_back(it->value);
+ }
- if (metadata.size > 0) {
- std::string field_value;
- s = storage_->Get(ctx, ctx.GetReadOptions(), sub_key, &field_value);
- if (!s.ok() && !s.IsNotFound()) return s;
+ std::vector<rocksdb::PinnableSlice> values_vector(keys.size());
+ std::vector<rocksdb::Status> statuses_vector(keys.size());
+ if (metadata.size > 0) {
+ rocksdb::ReadOptions read_options = ctx.DefaultMultiGetOptions();
+ storage_->MultiGet(ctx, read_options,
storage_->GetDB()->DefaultColumnFamily(), keys.size(), keys.data(),
+ values_vector.data(), statuses_vector.data());
+ }
- if (s.ok()) {
- if (nx || field_value == it->value) continue;
+ for (size_t field_index = 0; field_index < keys.size(); field_index++) {
+ const rocksdb::Slice field_key = keys[field_index];
+ bool exists = false;
+ if (metadata.size > 0) {
+ rocksdb::Status &field_status = statuses_vector[field_index];
+ if (!field_status.ok() && !field_status.IsNotFound()) {
+ return field_status;
+ }
+ if (field_status.ok()) {
+ if (nx || values_vector[field_index] == values[field_index]) {
+ continue;
+ }
exists = true;
}
}
- if (!exists) added++;
+ if (!exists) {
+ added++;
+ }
- s = batch->Put(sub_key, it->value);
+ s = batch->Put(field_key, values[field_index]);
if (!s.ok()) return s;
}