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;
   }
 

Reply via email to