This is an automated email from the ASF dual-hosted git repository.

eldenmoon pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 4eba7f9b0e8 [feature](storage) support change 
`variant_doc_materialization_min_rows ` and 
`vertical_compaction_num_columns_per_group ` (#61100)
4eba7f9b0e8 is described below

commit 4eba7f9b0e859680e98b7bf60d9bf2a64b9abcdb
Author: Chenyang Sun <[email protected]>
AuthorDate: Thu Mar 12 20:01:21 2026 +0800

    [feature](storage) support change `variant_doc_materialization_min_rows ` 
and `vertical_compaction_num_columns_per_group ` (#61100)
    
    ### 1. `variant_doc_materialization_min_rows`
    - Remove the restriction that prevented modifying this variant column
    property via `ALTER TABLE MODIFY COLUMN`.
    - This property controls the minimum row threshold for materializing
    variant sub-columns. The change is propagated via Light Schema Change.
    
    ### 2. `vertical_compaction_num_columns_per_group`
    - Allow modifying this table property via `ALTER TABLE SET(...)`. This
    controls how many columns are merged in each group during vertical
    compaction.
    - For wide variant tables, reducing this value significantly reduces
    peak
    memory usage during compaction.
---
 be/src/agent/task_worker_pool.cpp                  |   5 +
 be/src/cloud/cloud_tablet.cpp                      |   8 ++
 be/src/cloud/pb_convert.cpp                        |   8 ++
 be/src/storage/merger.cpp                          |  34 ++++-
 be/src/storage/merger.h                            |   3 +-
 be/src/storage/rowset/segcompaction.cpp            |  12 +-
 be/src/storage/tablet/tablet_meta.cpp              |  18 ++-
 be/src/storage/tablet/tablet_meta.h                |  11 +-
 cloud/src/meta-service/meta_service.cpp            |   3 +
 .../org/apache/doris/alter/CloudRollupJobV2.java   |   3 +-
 .../apache/doris/alter/CloudSchemaChangeJobV2.java |   3 +-
 .../java/org/apache/doris/alter/RollupJobV2.java   |   3 +-
 .../apache/doris/alter/SchemaChangeHandler.java    |  26 +++-
 .../org/apache/doris/alter/SchemaChangeJobV2.java  |   3 +-
 .../java/org/apache/doris/backup/RestoreJob.java   |   3 +-
 .../main/java/org/apache/doris/catalog/Column.java |   3 -
 .../main/java/org/apache/doris/catalog/Env.java    |   9 ++
 .../java/org/apache/doris/catalog/OlapTable.java   |  15 ++
 .../org/apache/doris/catalog/TableProperty.java    |  14 ++
 .../cloud/alter/CloudSchemaChangeHandler.java      |  19 +++
 .../apache/doris/cloud/backup/CloudRestoreJob.java |   3 +-
 .../cloud/datasource/CloudInternalCatalog.java     |   7 +-
 .../apache/doris/common/util/PropertyAnalyzer.java |  27 ++++
 .../apache/doris/datasource/InternalCatalog.java   |  14 +-
 .../org/apache/doris/master/ReportHandler.java     |   3 +-
 .../commands/info/ModifyTablePropertiesOp.java     |  17 +++
 .../org/apache/doris/task/CreateReplicaTask.java   |   7 +-
 .../doris/task/UpdateTabletMetaInfoTask.java       |   8 +-
 .../java/org/apache/doris/task/AgentTaskTest.java  |   2 +-
 gensrc/proto/cloud.proto                           |   1 +
 gensrc/proto/olap_file.proto                       |   2 +
 gensrc/thrift/AgentService.thrift                  |   2 +
 .../data/query_p0/system/test_table_properties.out |   8 +-
 ...variant_modify_doc_materialization_min_rows.out |  22 +++
 ...ertical_compaction_num_columns_per_group.groovy | 156 +++++++++++++++++++++
 ...iant_modify_doc_materialization_min_rows.groovy |  98 +++++++++++++
 36 files changed, 546 insertions(+), 34 deletions(-)

diff --git a/be/src/agent/task_worker_pool.cpp 
b/be/src/agent/task_worker_pool.cpp
index 52bd24f7d62..415c8b6742a 100644
--- a/be/src/agent/task_worker_pool.cpp
+++ b/be/src/agent/task_worker_pool.cpp
@@ -964,6 +964,11 @@ void update_tablet_meta_callback(StorageEngine& engine, 
const TAgentTaskRequest&
                     tablet_meta_info.time_series_compaction_level_threshold);
             need_to_save = true;
         }
+        if 
(tablet_meta_info.__isset.vertical_compaction_num_columns_per_group) {
+            
tablet->tablet_meta()->set_vertical_compaction_num_columns_per_group(
+                    
tablet_meta_info.vertical_compaction_num_columns_per_group);
+            need_to_save = true;
+        }
         if (tablet_meta_info.__isset.replica_id) {
             tablet->tablet_meta()->set_replica_id(tablet_meta_info.replica_id);
         }
diff --git a/be/src/cloud/cloud_tablet.cpp b/be/src/cloud/cloud_tablet.cpp
index ee835281acc..eb07def6006 100644
--- a/be/src/cloud/cloud_tablet.cpp
+++ b/be/src/cloud/cloud_tablet.cpp
@@ -1395,6 +1395,14 @@ Status CloudTablet::sync_meta() {
         _tablet_meta->mutable_tablet_schema()->set_disable_auto_compaction(
                 new_disable_auto_compaction);
     }
+    // Sync vertical_compaction_num_columns_per_group
+    auto new_vertical_compaction_num_columns_per_group =
+            tablet_meta->vertical_compaction_num_columns_per_group();
+    if (_tablet_meta->vertical_compaction_num_columns_per_group() !=
+        new_vertical_compaction_num_columns_per_group) {
+        _tablet_meta->set_vertical_compaction_num_columns_per_group(
+                new_vertical_compaction_num_columns_per_group);
+    }
 
     return Status::OK();
 }
diff --git a/be/src/cloud/pb_convert.cpp b/be/src/cloud/pb_convert.cpp
index 03afbdc207c..157a303c2c6 100644
--- a/be/src/cloud/pb_convert.cpp
+++ b/be/src/cloud/pb_convert.cpp
@@ -648,6 +648,8 @@ void doris_tablet_meta_to_cloud(TabletMetaCloudPB* out, 
const TabletMetaPB& in)
     out->set_time_series_compaction_empty_rowsets_threshold(
             in.time_series_compaction_empty_rowsets_threshold());
     
out->set_time_series_compaction_level_threshold(in.time_series_compaction_level_threshold());
+    out->set_vertical_compaction_num_columns_per_group(
+            in.vertical_compaction_num_columns_per_group());
     out->set_index_id(in.index_id());
     out->set_is_in_memory(in.is_in_memory());
     out->set_is_persistent(in.is_persistent());
@@ -725,6 +727,8 @@ void doris_tablet_meta_to_cloud(TabletMetaCloudPB* out, 
TabletMetaPB&& in) {
     out->set_time_series_compaction_empty_rowsets_threshold(
             in.time_series_compaction_empty_rowsets_threshold());
     
out->set_time_series_compaction_level_threshold(in.time_series_compaction_level_threshold());
+    out->set_vertical_compaction_num_columns_per_group(
+            in.vertical_compaction_num_columns_per_group());
     out->set_index_id(in.index_id());
     out->set_is_in_memory(in.is_in_memory());
     out->set_is_persistent(in.is_persistent());
@@ -809,6 +813,8 @@ void cloud_tablet_meta_to_doris(TabletMetaPB* out, const 
TabletMetaCloudPB& in)
     out->set_time_series_compaction_empty_rowsets_threshold(
             in.time_series_compaction_empty_rowsets_threshold());
     
out->set_time_series_compaction_level_threshold(in.time_series_compaction_level_threshold());
+    out->set_vertical_compaction_num_columns_per_group(
+            in.vertical_compaction_num_columns_per_group());
     out->set_index_id(in.index_id());
     out->set_is_in_memory(in.is_in_memory());
     out->set_is_persistent(in.is_persistent());
@@ -886,6 +892,8 @@ void cloud_tablet_meta_to_doris(TabletMetaPB* out, 
TabletMetaCloudPB&& in) {
     out->set_time_series_compaction_empty_rowsets_threshold(
             in.time_series_compaction_empty_rowsets_threshold());
     
out->set_time_series_compaction_level_threshold(in.time_series_compaction_level_threshold());
+    out->set_vertical_compaction_num_columns_per_group(
+            in.vertical_compaction_num_columns_per_group());
     out->set_index_id(in.index_id());
     out->set_is_in_memory(in.is_in_memory());
     out->set_is_persistent(in.is_persistent());
diff --git a/be/src/storage/merger.cpp b/be/src/storage/merger.cpp
index 3fbda7369e7..364ddffe579 100644
--- a/be/src/storage/merger.cpp
+++ b/be/src/storage/merger.cpp
@@ -167,7 +167,8 @@ Status Merger::vmerge_rowsets(BaseTabletSPtr tablet, 
ReaderType reader_type,
 // unique_key should consider sequence&delete column
 void Merger::vertical_split_columns(const TabletSchema& tablet_schema,
                                     std::vector<std::vector<uint32_t>>* 
column_groups,
-                                    std::vector<uint32_t>* 
key_group_cluster_key_idxes) {
+                                    std::vector<uint32_t>* 
key_group_cluster_key_idxes,
+                                    int32_t num_columns_per_group) {
     size_t num_key_cols = tablet_schema.num_key_columns();
     size_t total_cols = tablet_schema.num_columns();
     std::vector<uint32_t> key_columns;
@@ -227,8 +228,7 @@ void Merger::vertical_split_columns(const TabletSchema& 
tablet_schema,
             continue;
         }
 
-        if (!value_columns.empty() &&
-            value_columns.size() % 
config::vertical_compaction_num_columns_per_group == 0) {
+        if (!value_columns.empty() && value_columns.size() % 
num_columns_per_group == 0) {
             column_groups->push_back(value_columns);
             value_columns.clear();
         }
@@ -478,7 +478,33 @@ Status Merger::vertical_merge_rowsets(BaseTabletSPtr 
tablet, ReaderType reader_t
     LOG(INFO) << "Start to do vertical compaction, tablet_id: " << 
tablet->tablet_id();
     std::vector<std::vector<uint32_t>> column_groups;
     std::vector<uint32_t> key_group_cluster_key_idxes;
-    vertical_split_columns(tablet_schema, &column_groups, 
&key_group_cluster_key_idxes);
+    // If BE config vertical_compaction_num_columns_per_group has been 
modified from
+    // its default value (5), use the BE config; otherwise use the tablet meta 
value.
+    constexpr int32_t default_num_columns_per_group = 5;
+    int32_t num_columns_per_group =
+            config::vertical_compaction_num_columns_per_group != 
default_num_columns_per_group
+                    ? config::vertical_compaction_num_columns_per_group
+                    : 
tablet->tablet_meta()->vertical_compaction_num_columns_per_group();
+
+    
DBUG_EXECUTE_IF("Merger.vertical_merge_rowsets.check_num_columns_per_group", {
+        auto expected_value = 
DebugPoints::instance()->get_debug_param_or_default<int32_t>(
+                "Merger.vertical_merge_rowsets.check_num_columns_per_group", 
"expected_value", -1);
+        auto expected_tablet_id = 
DebugPoints::instance()->get_debug_param_or_default<int64_t>(
+                "Merger.vertical_merge_rowsets.check_num_columns_per_group", 
"tablet_id", -1);
+        if (expected_tablet_id != -1 && expected_tablet_id == 
tablet->tablet_id()) {
+            if (expected_value != -1 && expected_value != 
num_columns_per_group) {
+                LOG(FATAL) << "DEBUG_POINT CHECK FAILED: expected 
num_columns_per_group="
+                           << expected_value << " but got " << 
num_columns_per_group
+                           << " for tablet_id=" << tablet->tablet_id();
+            } else {
+                LOG(INFO) << "DEBUG_POINT CHECK PASSED: num_columns_per_group="
+                          << num_columns_per_group << ", tablet_id=" << 
tablet->tablet_id();
+            }
+        }
+    });
+
+    vertical_split_columns(tablet_schema, &column_groups, 
&key_group_cluster_key_idxes,
+                           num_columns_per_group);
 
     // Calculate total rows for density calculation after compaction
     int64_t total_rows = 0;
diff --git a/be/src/storage/merger.h b/be/src/storage/merger.h
index 7d346e98b67..62d9e4fed0e 100644
--- a/be/src/storage/merger.h
+++ b/be/src/storage/merger.h
@@ -71,7 +71,8 @@ public:
     // for vertical compaction
     static void vertical_split_columns(const TabletSchema& tablet_schema,
                                        std::vector<std::vector<uint32_t>>* 
column_groups,
-                                       std::vector<uint32_t>* 
key_group_cluster_key_idxes);
+                                       std::vector<uint32_t>* 
key_group_cluster_key_idxes,
+                                       int32_t num_columns_per_group);
     static Status vertical_compact_one_group(
             BaseTabletSPtr tablet, ReaderType reader_type, const TabletSchema& 
tablet_schema,
             bool is_key, const std::vector<uint32_t>& column_group,
diff --git a/be/src/storage/rowset/segcompaction.cpp 
b/be/src/storage/rowset/segcompaction.cpp
index df0788b273e..e94fabee96e 100644
--- a/be/src/storage/rowset/segcompaction.cpp
+++ b/be/src/storage/rowset/segcompaction.cpp
@@ -34,6 +34,7 @@
 
 #include "absl/strings/substitute.h"
 #include "common/compiler_util.h" // IWYU pragma: keep
+#include "common/config.h"
 #include "common/logging.h"
 #include "io/fs/file_system.h"
 #include "io/fs/file_writer.h"
@@ -281,8 +282,15 @@ Status 
SegcompactionWorker::_do_compact_segments(SegCompactionCandidatesSharedPt
 
     std::vector<std::vector<uint32_t>> column_groups;
     std::vector<uint32_t> key_group_cluster_key_idxes;
-    Merger::vertical_split_columns(*ctx.tablet_schema, &column_groups,
-                                   &key_group_cluster_key_idxes);
+    // If BE config vertical_compaction_num_columns_per_group has been 
modified from
+    // its default value (5), use the BE config; otherwise use the tablet meta 
value.
+    constexpr int32_t default_num_columns_per_group = 5;
+    int32_t num_columns_per_group =
+            config::vertical_compaction_num_columns_per_group != 
default_num_columns_per_group
+                    ? config::vertical_compaction_num_columns_per_group
+                    : 
tablet->tablet_meta()->vertical_compaction_num_columns_per_group();
+    Merger::vertical_split_columns(*ctx.tablet_schema, &column_groups, 
&key_group_cluster_key_idxes,
+                                   num_columns_per_group);
     RowSourcesBuffer row_sources_buf(tablet->tablet_id(), 
tablet->tablet_path(),
                                      ReaderType::READER_SEGMENT_COMPACTION);
 
diff --git a/be/src/storage/tablet/tablet_meta.cpp 
b/be/src/storage/tablet/tablet_meta.cpp
index 4c032605dbe..b2b6d803d79 100644
--- a/be/src/storage/tablet/tablet_meta.cpp
+++ b/be/src/storage/tablet/tablet_meta.cpp
@@ -125,7 +125,10 @@ TabletMetaSharedPtr TabletMeta::create(
             request.time_series_compaction_time_threshold_seconds,
             request.time_series_compaction_empty_rowsets_threshold,
             request.time_series_compaction_level_threshold, 
inverted_index_file_storage_format,
-            request.tde_algorithm, storage_format);
+            request.tde_algorithm, storage_format,
+            request.__isset.vertical_compaction_num_columns_per_group
+                    ? request.vertical_compaction_num_columns_per_group
+                    : 5);
 }
 
 TabletMeta::~TabletMeta() {
@@ -154,7 +157,8 @@ TabletMeta::TabletMeta(int64_t table_id, int64_t 
partition_id, int64_t tablet_id
                        int64_t time_series_compaction_level_threshold,
                        TInvertedIndexFileStorageFormat::type 
inverted_index_file_storage_format,
                        TEncryptionAlgorithm::type tde_algorithm,
-                       TStorageFormat::type storage_format)
+                       TStorageFormat::type storage_format,
+                       int32_t vertical_compaction_num_columns_per_group)
         : _tablet_uid(0, 0),
           _schema(new TabletSchema),
           _delete_bitmap(new DeleteBitmap(tablet_id)),
@@ -187,6 +191,8 @@ TabletMeta::TabletMeta(int64_t table_id, int64_t 
partition_id, int64_t tablet_id
             time_series_compaction_empty_rowsets_threshold);
     tablet_meta_pb.set_time_series_compaction_level_threshold(
             time_series_compaction_level_threshold);
+    tablet_meta_pb.set_vertical_compaction_num_columns_per_group(
+            vertical_compaction_num_columns_per_group);
     TabletSchemaPB* schema = tablet_meta_pb.mutable_schema();
     schema->set_num_short_key_columns(tablet_schema.short_key_column_count);
     
schema->set_num_rows_per_row_block(config::default_num_rows_per_column_file_block);
@@ -464,7 +470,9 @@ TabletMeta::TabletMeta(const TabletMeta& b)
                   b._time_series_compaction_time_threshold_seconds),
           _time_series_compaction_empty_rowsets_threshold(
                   b._time_series_compaction_empty_rowsets_threshold),
-          
_time_series_compaction_level_threshold(b._time_series_compaction_level_threshold)
 {};
+          
_time_series_compaction_level_threshold(b._time_series_compaction_level_threshold),
+          _vertical_compaction_num_columns_per_group(
+                  b._vertical_compaction_num_columns_per_group) {};
 
 void TabletMeta::init_column_from_tcolumn(uint32_t unique_id, const TColumn& 
tcolumn,
                                           ColumnPB* column) {
@@ -857,6 +865,8 @@ void TabletMeta::init_from_pb(const TabletMetaPB& 
tablet_meta_pb) {
             tablet_meta_pb.time_series_compaction_empty_rowsets_threshold();
     _time_series_compaction_level_threshold =
             tablet_meta_pb.time_series_compaction_level_threshold();
+    _vertical_compaction_num_columns_per_group =
+            tablet_meta_pb.vertical_compaction_num_columns_per_group();
 
     if (tablet_meta_pb.has_encryption_algorithm()) {
         _encryption_algorithm = tablet_meta_pb.encryption_algorithm();
@@ -952,6 +962,8 @@ void TabletMeta::to_meta_pb(TabletMetaPB* tablet_meta_pb, 
bool cloud_get_rowset_
             time_series_compaction_empty_rowsets_threshold());
     tablet_meta_pb->set_time_series_compaction_level_threshold(
             time_series_compaction_level_threshold());
+    tablet_meta_pb->set_vertical_compaction_num_columns_per_group(
+            vertical_compaction_num_columns_per_group());
 
     tablet_meta_pb->set_encryption_algorithm(_encryption_algorithm);
 }
diff --git a/be/src/storage/tablet/tablet_meta.h 
b/be/src/storage/tablet/tablet_meta.h
index 1cdcba3783d..046f0ca6f8a 100644
--- a/be/src/storage/tablet/tablet_meta.h
+++ b/be/src/storage/tablet/tablet_meta.h
@@ -118,7 +118,8 @@ public:
                TInvertedIndexFileStorageFormat::type 
inverted_index_file_storage_format =
                        TInvertedIndexFileStorageFormat::V2,
                TEncryptionAlgorithm::type tde_algorithm = 
TEncryptionAlgorithm::PLAINTEXT,
-               TStorageFormat::type storage_format = TStorageFormat::V2);
+               TStorageFormat::type storage_format = TStorageFormat::V2,
+               int32_t vertical_compaction_num_columns_per_group = 5);
     // If need add a filed in TableMeta, filed init copy in copy construct 
function
     TabletMeta(const TabletMeta& tablet_meta);
     TabletMeta(TabletMeta&& tablet_meta) = delete;
@@ -296,6 +297,13 @@ public:
         return _time_series_compaction_level_threshold;
     }
 
+    void set_vertical_compaction_num_columns_per_group(int32_t num) {
+        _vertical_compaction_num_columns_per_group = num;
+    }
+    int32_t vertical_compaction_num_columns_per_group() const {
+        return _vertical_compaction_num_columns_per_group;
+    }
+
     int64_t ttl_seconds() const {
         std::shared_lock rlock(_meta_lock);
         return _ttl_seconds;
@@ -366,6 +374,7 @@ private:
     int64_t _time_series_compaction_time_threshold_seconds = 0;
     int64_t _time_series_compaction_empty_rowsets_threshold = 0;
     int64_t _time_series_compaction_level_threshold = 0;
+    int32_t _vertical_compaction_num_columns_per_group = 5;
 
     int64_t _avg_rs_meta_serialize_size = 0;
 
diff --git a/cloud/src/meta-service/meta_service.cpp 
b/cloud/src/meta-service/meta_service.cpp
index 90ebd4076e1..23927ba99b3 100644
--- a/cloud/src/meta-service/meta_service.cpp
+++ b/cloud/src/meta-service/meta_service.cpp
@@ -1245,6 +1245,9 @@ void 
MetaServiceImpl::update_tablet(::google::protobuf::RpcController* controlle
                     }
                 }
             }
+        } else if 
(tablet_meta_info.has_vertical_compaction_num_columns_per_group()) {
+            tablet_meta.set_vertical_compaction_num_columns_per_group(
+                    
tablet_meta_info.vertical_compaction_num_columns_per_group());
         }
         int64_t table_id = tablet_meta.table_id();
         int64_t index_id = tablet_meta.index_id();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/alter/CloudRollupJobV2.java 
b/fe/fe-core/src/main/java/org/apache/doris/alter/CloudRollupJobV2.java
index dcf57777f4f..8c6a306aedb 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/alter/CloudRollupJobV2.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/alter/CloudRollupJobV2.java
@@ -235,7 +235,8 @@ public class CloudRollupJobV2 extends RollupJobV2 {
                                     tbl.variantEnableFlattenNested(), null,
                                     tbl.storagePageSize(), 
tbl.getTDEAlgorithmPB(),
                                     tbl.storageDictPageSize(), true,
-                                    tbl.getColumnSeqMapping());
+                                    tbl.getColumnSeqMapping(),
+                                    
tbl.getVerticalCompactionNumColumnsPerGroup());
                 requestBuilder.addTabletMetas(builder);
             } // end for rollupTablets
             requestBuilder.setDbId(dbId);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/alter/CloudSchemaChangeJobV2.java 
b/fe/fe-core/src/main/java/org/apache/doris/alter/CloudSchemaChangeJobV2.java
index 3fdc176fc5f..9387a93aaa9 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/alter/CloudSchemaChangeJobV2.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/alter/CloudSchemaChangeJobV2.java
@@ -258,7 +258,8 @@ public class CloudSchemaChangeJobV2 extends 
SchemaChangeJobV2 {
                                             tbl.variantEnableFlattenNested(), 
clusterKeyUids,
                                             tbl.storagePageSize(), 
tbl.getTDEAlgorithmPB(),
                                             tbl.storageDictPageSize(), true,
-                                            columnSeqMapping);
+                                            columnSeqMapping,
+                                                    
tbl.getVerticalCompactionNumColumnsPerGroup());
                     requestBuilder.addTabletMetas(builder);
                 } // end for rollupTablets
                 requestBuilder.setDbId(dbId);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/alter/RollupJobV2.java 
b/fe/fe-core/src/main/java/org/apache/doris/alter/RollupJobV2.java
index 66bfaedc8a7..27a1fdcc8df 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/alter/RollupJobV2.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/alter/RollupJobV2.java
@@ -272,7 +272,8 @@ public class RollupJobV2 extends AlterJobV2 implements 
GsonPostProcessable {
                                 tbl.rowStorePageSize(),
                                 tbl.variantEnableFlattenNested(),
                                 tbl.storagePageSize(), tbl.getTDEAlgorithm(),
-                                tbl.storageDictPageSize(), null);
+                                tbl.storageDictPageSize(), null,
+                                tbl.getVerticalCompactionNumColumnsPerGroup());
                         
createReplicaTask.setBaseTablet(tabletIdMap.get(rollupTabletId), 
baseSchemaHash);
                         if (this.storageFormat != null) {
                             
createReplicaTask.setStorageFormat(this.storageFormat);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java 
b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
index 1c5c6529ba5..113f32d7ae8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
@@ -825,6 +825,11 @@ public class SchemaChangeHandler extends AlterHandler {
                         
ColumnType.checkSupportSchemaChangeForComplexType(col.getType(), 
modColumn.getType(), true);
                         lightSchemaChange = 
olapTable.getEnableLightSchemaChange();
                     }
+                    // variant property-only change (e.g. 
variant_doc_materialization_min_rows)
+                    if (columnPos == null && col.getDataType() == 
PrimitiveType.VARIANT
+                            && modColumn.getDataType() == 
PrimitiveType.VARIANT) {
+                        lightSchemaChange = 
olapTable.getEnableLightSchemaChange();
+                    }
                     if (col.isClusterKey()) {
                         throw new DdlException("Can not modify cluster key 
column: " + col.getName());
                     }
@@ -2572,6 +2577,7 @@ public class SchemaChangeHandler extends AlterHandler {
                 add(PropertyAnalyzer.PROPERTIES_SKIP_WRITE_INDEX_ON_LOAD);
                 
add(PropertyAnalyzer.PROPERTIES_TIME_SERIES_COMPACTION_EMPTY_ROWSETS_THRESHOLD);
                 
add(PropertyAnalyzer.PROPERTIES_TIME_SERIES_COMPACTION_LEVEL_THRESHOLD);
+                
add(PropertyAnalyzer.PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP);
                 add(PropertyAnalyzer.PROPERTIES_AUTO_ANALYZE_POLICY);
                 add(PropertyAnalyzer.PROPERTIES_STORAGE_MEDIUM);
                 add(PropertyAnalyzer.PROPERTIES_PARTITION_RETENTION_COUNT);
@@ -2663,7 +2669,14 @@ public class SchemaChangeHandler extends AlterHandler {
         }
 
 
+        int verticalCompactionNumColumnsPerGroup = -1; // < 0 means don't 
update
+        if 
(properties.containsKey(PropertyAnalyzer.PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP))
 {
+            verticalCompactionNumColumnsPerGroup = Integer.parseInt(
+                    
properties.get(PropertyAnalyzer.PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP));
+        }
+
         if (isInMemory < 0 && storagePolicyId < 0 && compactionPolicy == null 
&& timeSeriesCompactionConfig.isEmpty()
+                && verticalCompactionNumColumnsPerGroup < 0
                 && 
!properties.containsKey(PropertyAnalyzer.PROPERTIES_IS_BEING_SYNCED)
                 && 
!properties.containsKey(PropertyAnalyzer.PROPERTIES_ENABLE_MOW_LIGHT_DELETE)
                 && 
!properties.containsKey(PropertyAnalyzer.PROPERTIES_ENABLE_SINGLE_REPLICA_COMPACTION)
@@ -2711,7 +2724,7 @@ public class SchemaChangeHandler extends AlterHandler {
         for (Partition partition : partitions) {
             updatePartitionProperties(db, olapTable.getName(), 
partition.getName(), storagePolicyId, isInMemory,
                                     null, compactionPolicy, 
timeSeriesCompactionConfig, enableSingleCompaction, skip,
-                                    disableAutoCompaction);
+                                    disableAutoCompaction, 
verticalCompactionNumColumnsPerGroup);
         }
 
         olapTable.writeLockOrDdlException();
@@ -2759,8 +2772,8 @@ public class SchemaChangeHandler extends AlterHandler {
 
         for (String partitionName : partitionNames) {
             try {
-                updatePartitionProperties(db, olapTable.getName(), 
partitionName, storagePolicyId,
-                                                                            
isInMemory, null, null, null, -1, -1, -1);
+                updatePartitionProperties(db, olapTable.getName(), 
partitionName,
+                        storagePolicyId, isInMemory, null, null, null, -1, -1, 
-1, -1);
             } catch (Exception e) {
                 String errMsg = "Failed to update partition[" + partitionName 
+ "]'s 'in_memory' property. "
                         + "The reason is [" + e.getMessage() + "]";
@@ -2777,7 +2790,8 @@ public class SchemaChangeHandler extends AlterHandler {
                                           int isInMemory, BinlogConfig 
binlogConfig, String compactionPolicy,
                                           Map<String, Long> 
timeSeriesCompactionConfig,
                                           int enableSingleCompaction, int 
skipWriteIndexOnLoad,
-                                          int disableAutoCompaction) throws 
UserException {
+                                          int disableAutoCompaction,
+                                          int 
verticalCompactionNumColumnsPerGroup) throws UserException {
         // be id -> <tablet id,schemaHash>
         Map<Long, Set<Pair<Long, Integer>>> beIdToTabletIdWithHash = 
Maps.newHashMap();
         OlapTable olapTable = (OlapTable) 
db.getTableOrMetaException(tableName, Table.TableType.OLAP);
@@ -2811,7 +2825,7 @@ public class SchemaChangeHandler extends AlterHandler {
             UpdateTabletMetaInfoTask task = new 
UpdateTabletMetaInfoTask(kv.getKey(), kv.getValue(), isInMemory,
                                             storagePolicyId, binlogConfig, 
countDownLatch, compactionPolicy,
                                             timeSeriesCompactionConfig, 
enableSingleCompaction, skipWriteIndexOnLoad,
-                                            disableAutoCompaction);
+                                            disableAutoCompaction, 
verticalCompactionNumColumnsPerGroup);
             batchTask.addTask(task);
         }
         if (!FeConstants.runningUnitTest) {
@@ -3694,7 +3708,7 @@ public class SchemaChangeHandler extends AlterHandler {
 
         for (Partition partition : partitions) {
             updatePartitionProperties(db, olapTable.getName(), 
partition.getName(), -1, -1,
-                    newBinlogConfig, null, null, -1, -1, -1);
+                    newBinlogConfig, null, null, -1, -1, -1, -1);
         }
 
         olapTable.writeLockOrDdlException();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeJobV2.java 
b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeJobV2.java
index 00cfc7dc8a1..955af7162fd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeJobV2.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeJobV2.java
@@ -335,7 +335,8 @@ public class SchemaChangeJobV2 extends AlterJobV2 
implements GsonPostProcessable
                                     tbl.variantEnableFlattenNested(),
                                     tbl.storagePageSize(), 
tbl.getTDEAlgorithm(),
                                     tbl.storageDictPageSize(),
-                                    columnSeqMapping);
+                                    columnSeqMapping,
+                                    
tbl.getVerticalCompactionNumColumnsPerGroup());
 
                             
createReplicaTask.setBaseTablet(partitionIndexTabletMap.get(partitionId, 
shadowIdxId)
                                     .get(shadowTabletId), originSchemaHash);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/backup/RestoreJob.java 
b/fe/fe-core/src/main/java/org/apache/doris/backup/RestoreJob.java
index 2874dc900c8..60142fa694f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/backup/RestoreJob.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/backup/RestoreJob.java
@@ -1454,7 +1454,8 @@ public class RestoreJob extends AbstractJob implements 
GsonPostProcessable {
                             localTbl.variantEnableFlattenNested(),
                             localTbl.storagePageSize(), 
localTbl.getTDEAlgorithm(),
                             localTbl.storageDictPageSize(),
-                            localTbl.getColumnSeqMapping());
+                            localTbl.getColumnSeqMapping(),
+                            
localTbl.getVerticalCompactionNumColumnsPerGroup());
                     
task.setInvertedIndexFileStorageFormat(localTbl.getInvertedIndexFileStorageFormat());
                     task.setInRestoreMode(true);
                     if (baseTabletRef != null) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Column.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Column.java
index e06d0c93d28..d3aa5aa5917 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Column.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Column.java
@@ -977,9 +977,6 @@ public class Column implements GsonPostProcessable {
             if (this.getVariantEnableDocMode() != 
other.getVariantEnableDocMode()) {
                 throw new DdlException("Can not change variant enable doc 
snapshot mode");
             }
-            if (this.getvariantDocMaterializationMinRows() != 
other.getvariantDocMaterializationMinRows()) {
-                throw new DdlException("Can not change variant doc snapshot 
min rows");
-            }
             if (this.getVariantDocShardCount() != 
other.getVariantDocShardCount()) {
                 throw new DdlException("Can not change variant doc snapshot 
shard count");
             }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
index 37e0b7886b4..77c1187effe 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
@@ -3986,6 +3986,14 @@ public class Env {
             
sb.append(olapTable.getTimeSeriesCompactionLevelThreshold()).append("\"");
         }
 
+        // vertical compaction num columns per group
+        if (olapTable.getVerticalCompactionNumColumnsPerGroup()
+                != 
PropertyAnalyzer.VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP_DEFAULT_VALUE) {
+            
sb.append(",\n\"").append(PropertyAnalyzer.PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP)
+                    .append("\" = \"");
+            
sb.append(olapTable.getVerticalCompactionNumColumnsPerGroup()).append("\"");
+        }
+
         // Storage Vault
         if (!Strings.isNullOrEmpty(olapTable.getStorageVaultId())) {
             sb.append(",\n\"").append(PropertyAnalyzer
@@ -6222,6 +6230,7 @@ public class Env {
                 .buildEnableSingleReplicaCompaction()
                 .buildTimeSeriesCompactionEmptyRowsetsThreshold()
                 .buildTimeSeriesCompactionLevelThreshold()
+                .buildVerticalCompactionNumColumnsPerGroup()
                 .buildTTLSeconds()
                 .buildAutoAnalyzeProperty()
                 .buildPartitionRetentionCount();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
index ca3b5cc766c..56d24644b81 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
@@ -2735,6 +2735,21 @@ public class OlapTable extends Table implements 
MTMVRelatedTableIf, GsonPostProc
         return 
PropertyAnalyzer.TIME_SERIES_COMPACTION_LEVEL_THRESHOLD_DEFAULT_VALUE;
     }
 
+    public void setVerticalCompactionNumColumnsPerGroup(int 
verticalCompactionNumColumnsPerGroup) {
+        TableProperty tableProperty = getOrCreatTableProperty();
+        tableProperty.modifyTableProperties(
+                
PropertyAnalyzer.PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP,
+                
Integer.valueOf(verticalCompactionNumColumnsPerGroup).toString());
+        tableProperty.buildVerticalCompactionNumColumnsPerGroup();
+    }
+
+    public int getVerticalCompactionNumColumnsPerGroup() {
+        if (tableProperty != null) {
+            return tableProperty.verticalCompactionNumColumnsPerGroup();
+        }
+        return 
PropertyAnalyzer.VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP_DEFAULT_VALUE;
+    }
+
     public int getIndexSchemaVersion(long indexId) {
         MaterializedIndexMeta indexMeta = indexIdToMeta.get(indexId);
         return indexMeta.getSchemaVersion();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/TableProperty.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/TableProperty.java
index ea770b20a3b..f8544de47cf 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/TableProperty.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/TableProperty.java
@@ -98,6 +98,8 @@ public class TableProperty implements GsonPostProcessable {
 
     private boolean enableSingleReplicaCompaction = false;
 
+    private int verticalCompactionNumColumnsPerGroup = 5;
+
     private boolean storeRowColumn = false;
 
     private boolean skipWriteIndexOnLoad = false;
@@ -167,6 +169,7 @@ public class TableProperty implements GsonPostProcessable {
                 buildTimeSeriesCompactionTimeThresholdSeconds();
                 buildSkipWriteIndexOnLoad();
                 buildEnableSingleReplicaCompaction();
+                buildVerticalCompactionNumColumnsPerGroup();
                 buildDisableAutoCompaction();
                 buildTimeSeriesCompactionEmptyRowsetsThreshold();
                 buildTimeSeriesCompactionLevelThreshold();
@@ -343,6 +346,16 @@ public class TableProperty implements GsonPostProcessable {
         return enableSingleReplicaCompaction;
     }
 
+    public TableProperty buildVerticalCompactionNumColumnsPerGroup() {
+        verticalCompactionNumColumnsPerGroup = Integer.parseInt(
+                
properties.getOrDefault(PropertyAnalyzer.PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP,
 "5"));
+        return this;
+    }
+
+    public int verticalCompactionNumColumnsPerGroup() {
+        return verticalCompactionNumColumnsPerGroup;
+    }
+
     public TableProperty buildStoreRowColumn() {
         storeRowColumn = Boolean.parseBoolean(
                 
properties.getOrDefault(PropertyAnalyzer.PROPERTIES_STORE_ROW_COLUMN, "false"));
@@ -885,6 +898,7 @@ public class TableProperty implements GsonPostProcessable {
         buildTimeSeriesCompactionTimeThresholdSeconds();
         buildDisableAutoCompaction();
         buildEnableSingleReplicaCompaction();
+        buildVerticalCompactionNumColumnsPerGroup();
         buildTimeSeriesCompactionEmptyRowsetsThreshold();
         buildTimeSeriesCompactionLevelThreshold();
         buildTTLSeconds();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/cloud/alter/CloudSchemaChangeHandler.java
 
b/fe/fe-core/src/main/java/org/apache/doris/cloud/alter/CloudSchemaChangeHandler.java
index d199fcbb10e..caf5f93f772 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/cloud/alter/CloudSchemaChangeHandler.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/cloud/alter/CloudSchemaChangeHandler.java
@@ -113,6 +113,7 @@ public class CloudSchemaChangeHandler extends 
SchemaChangeHandler {
                 add(PropertyAnalyzer.PROPERTIES_ENABLE_MOW_LIGHT_DELETE);
                 add(PropertyAnalyzer.PROPERTIES_AUTO_ANALYZE_POLICY);
                 add(PropertyAnalyzer.PROPERTIES_PARTITION_RETENTION_COUNT);
+                
add(PropertyAnalyzer.PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP);
             }
         };
         List<String> notAllowedProps = properties.keySet().stream().filter(s 
-> !allowedProps.contains(s))
@@ -346,6 +347,18 @@ public class CloudSchemaChangeHandler extends 
SchemaChangeHandler {
             param.type = 
UpdatePartitionMetaParam.TabletMetaType.ENABLE_MOW_LIGHT_DELETE;
         } else if 
(properties.containsKey(PropertyAnalyzer.PROPERTIES_AUTO_ANALYZE_POLICY)) {
             // Do nothing.
+        } else if (properties.containsKey(
+                
PropertyAnalyzer.PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP)) {
+            int verticalCompactionNumColumnsPerGroup = 
Integer.parseInt(properties.get(
+                    
PropertyAnalyzer.PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP));
+            olapTable.readLock();
+            try {
+                partitions.addAll(olapTable.getPartitions());
+            } finally {
+                olapTable.readUnlock();
+            }
+            param.verticalCompactionNumColumnsPerGroup = 
verticalCompactionNumColumnsPerGroup;
+            param.type = 
UpdatePartitionMetaParam.TabletMetaType.VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP;
         } else {
             LOG.warn("invalid properties:{}", properties);
             throw new UserException("invalid properties");
@@ -381,6 +394,7 @@ public class CloudSchemaChangeHandler extends 
SchemaChangeHandler {
             TIME_SERIES_COMPACTION_LEVEL_THRESHOLD,
             DISABLE_AUTO_COMPACTION,
             ENABLE_MOW_LIGHT_DELETE,
+            VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP,
         }
 
         TabletMetaType type;
@@ -397,6 +411,7 @@ public class CloudSchemaChangeHandler extends 
SchemaChangeHandler {
         long timeSeriesCompactionLevelThreshold = 0;
         boolean disableAutoCompaction = false;
         boolean enableMowLightDelete = false;
+        int verticalCompactionNumColumnsPerGroup = 5;
     }
 
     public void updateCloudPartitionMeta(Database db,
@@ -477,6 +492,10 @@ public class CloudSchemaChangeHandler extends 
SchemaChangeHandler {
                                 param.enableMowLightDelete
                         );
                         break;
+                    case VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP:
+                        infoBuilder.setVerticalCompactionNumColumnsPerGroup(
+                                param.verticalCompactionNumColumnsPerGroup);
+                        break;
                     default:
                         throw new UserException("Unknown TabletMetaType");
                 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/cloud/backup/CloudRestoreJob.java 
b/fe/fe-core/src/main/java/org/apache/doris/cloud/backup/CloudRestoreJob.java
index f518d6cc3a4..980e75ee2b5 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/cloud/backup/CloudRestoreJob.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/cloud/backup/CloudRestoreJob.java
@@ -404,7 +404,8 @@ public class CloudRestoreJob extends RestoreJob {
                                     localTbl.variantEnableFlattenNested(), 
clusterKeyUids,
                                     localTbl.storagePageSize(), 
localTbl.getTDEAlgorithmPB(),
                                     localTbl.storageDictPageSize(), false,
-                                    localTbl.getColumnSeqMapping()));
+                                    localTbl.getColumnSeqMapping(),
+                                    
localTbl.getVerticalCompactionNumColumnsPerGroup()));
                         // In cloud mode all storage medium will be saved to 
HDD.
                         TabletMeta tabletMeta = new TabletMeta(db.getId(), 
localTbl.getId(), restorePart.getId(),
                                 restoredIdx.getId(), 
indexMeta.getSchemaHash(), TStorageMedium.HDD);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/cloud/datasource/CloudInternalCatalog.java
 
b/fe/fe-core/src/main/java/org/apache/doris/cloud/datasource/CloudInternalCatalog.java
index 9d47c68b844..173b0a1d57b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/cloud/datasource/CloudInternalCatalog.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/cloud/datasource/CloudInternalCatalog.java
@@ -192,7 +192,8 @@ public class CloudInternalCatalog extends InternalCatalog {
                         tbl.variantEnableFlattenNested(), clusterKeyUids,
                         tbl.storagePageSize(), tbl.getTDEAlgorithmPB(),
                         tbl.storageDictPageSize(), true,
-                        tbl.getColumnSeqMapping());
+                        tbl.getColumnSeqMapping(),
+                                    
tbl.getVerticalCompactionNumColumnsPerGroup());
                 requestBuilder.addTabletMetas(builder);
             }
             requestBuilder.setDbId(dbId);
@@ -226,7 +227,8 @@ public class CloudInternalCatalog extends InternalCatalog {
             TInvertedIndexFileStorageFormat invertedIndexFileStorageFormat, 
long pageSize,
             boolean variantEnableFlattenNested, List<Integer> clusterKeyUids,
             long storagePageSize, EncryptionAlgorithmPB encryptionAlgorithm, 
long storageDictPageSize,
-            boolean createInitialRowset, Map<String, List<String>> 
columnSeqMapping) throws DdlException {
+            boolean createInitialRowset, Map<String, List<String>> 
columnSeqMapping,
+            int verticalCompactionNumColumnsPerGroup) throws DdlException {
         OlapFile.TabletMetaCloudPB.Builder builder = 
OlapFile.TabletMetaCloudPB.newBuilder();
         builder.setTableId(tableId);
         builder.setIndexId(indexId);
@@ -261,6 +263,7 @@ public class CloudInternalCatalog extends InternalCatalog {
         
builder.setTimeSeriesCompactionTimeThresholdSeconds(timeSeriesCompactionTimeThresholdSeconds);
         
builder.setTimeSeriesCompactionEmptyRowsetsThreshold(timeSeriesCompactionEmptyRowsetsThreshold);
         
builder.setTimeSeriesCompactionLevelThreshold(timeSeriesCompactionLevelThreshold);
+        
builder.setVerticalCompactionNumColumnsPerGroup(verticalCompactionNumColumnsPerGroup);
 
         OlapFile.TabletSchemaCloudPB.Builder schemaBuilder = 
OlapFile.TabletSchemaCloudPB.newBuilder();
         schemaBuilder.setSchemaVersion(schemaVersion);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
index 02347cd38e7..93dc9756f2a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
@@ -161,6 +161,9 @@ public class PropertyAnalyzer {
 
     public static final String PROPERTIES_ENABLE_SINGLE_REPLICA_COMPACTION = 
"enable_single_replica_compaction";
 
+    public static final String 
PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP =
+            "vertical_compaction_num_columns_per_group";
+
     public static final String PROPERTIES_STORE_ROW_COLUMN = 
"store_row_column"; // deprecated
 
     public static final String PROPERTIES_ROW_STORE_COLUMNS = 
"row_store_columns";
@@ -255,6 +258,7 @@ public class PropertyAnalyzer {
     public static final long 
TIME_SERIES_COMPACTION_TIME_THRESHOLD_SECONDS_DEFAULT_VALUE = 3600;
     public static final long 
TIME_SERIES_COMPACTION_EMPTY_ROWSETS_THRESHOLD_DEFAULT_VALUE = 5;
     public static final long 
TIME_SERIES_COMPACTION_LEVEL_THRESHOLD_DEFAULT_VALUE = 1;
+    public static final int 
VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP_DEFAULT_VALUE = 5;
 
     public static final String PROPERTIES_VARIANT_MAX_SUBCOLUMNS_COUNT = 
"variant_max_subcolumns_count";
 
@@ -2250,4 +2254,27 @@ public class PropertyAnalyzer {
         }
         throw new AnalysisException("Invalid tde algorithm: " + name + ", only 
support AES256 and SM4 currently");
     }
+
+    public static Integer 
analyzeVerticalCompactionNumColumnsPerGroup(Map<String, String> properties)
+            throws AnalysisException {
+        if (properties == null || properties.isEmpty()) {
+            return VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP_DEFAULT_VALUE;
+        }
+        String value = 
properties.get(PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP);
+        if (null == value) {
+            return VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP_DEFAULT_VALUE;
+        }
+        
properties.remove(PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP);
+        try {
+            int num = Integer.parseInt(value);
+            if (num < 1 || num > 50) {
+                throw new 
AnalysisException(PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP
+                        + " must be between 1 and 50");
+            }
+            return num;
+        } catch (NumberFormatException e) {
+            throw new 
AnalysisException(PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP
+                    + " must be a valid integer");
+        }
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
index 61d2d2e2294..6a7a2950beb 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
@@ -2130,7 +2130,8 @@ public class InternalCatalog implements 
CatalogIf<Database> {
                             tbl.variantEnableFlattenNested(),
                             tbl.storagePageSize(), tbl.getTDEAlgorithm(),
                             tbl.storageDictPageSize(),
-                            tbl.getColumnSeqMapping());
+                            tbl.getColumnSeqMapping(),
+                            tbl.getVerticalCompactionNumColumnsPerGroup());
 
                     task.setStorageFormat(tbl.getStorageFormat());
                     
task.setInvertedIndexFileStorageFormat(tbl.getInvertedIndexFileStorageFormat());
@@ -2480,6 +2481,17 @@ public class InternalCatalog implements 
CatalogIf<Database> {
         }
         
olapTable.setTimeSeriesCompactionLevelThreshold(timeSeriesCompactionLevelThreshold);
 
+        // set vertical compaction num columns per group
+        int verticalCompactionNumColumnsPerGroup
+                = 
PropertyAnalyzer.VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP_DEFAULT_VALUE;
+        try {
+            verticalCompactionNumColumnsPerGroup = PropertyAnalyzer
+                .analyzeVerticalCompactionNumColumnsPerGroup(properties);
+        } catch (AnalysisException e) {
+            throw new DdlException(e.getMessage());
+        }
+        
olapTable.setVerticalCompactionNumColumnsPerGroup(verticalCompactionNumColumnsPerGroup);
+
         boolean variantEnableFlattenNested  = false;
         try {
             variantEnableFlattenNested = 
PropertyAnalyzer.analyzeVariantFlattenNested(properties);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/master/ReportHandler.java 
b/fe/fe-core/src/main/java/org/apache/doris/master/ReportHandler.java
index 93d0d10b055..cf7df808eda 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/master/ReportHandler.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/master/ReportHandler.java
@@ -1089,7 +1089,8 @@ public class ReportHandler extends Daemon {
                                             
olapTable.variantEnableFlattenNested(),
                                             olapTable.storagePageSize(), 
olapTable.getTDEAlgorithm(),
                                             olapTable.storageDictPageSize(),
-                                            olapTable.getColumnSeqMapping());
+                                            olapTable.getColumnSeqMapping(),
+                                            
olapTable.getVerticalCompactionNumColumnsPerGroup());
                                     createReplicaTask.setIsRecoverTask(true);
                                     
createReplicaTask.setInvertedIndexFileStorageFormat(olapTable
                                                                 
.getInvertedIndexFileStorageFormat());
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/ModifyTablePropertiesOp.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/ModifyTablePropertiesOp.java
index 7dfc89e96e4..e2189ed0696 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/ModifyTablePropertiesOp.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/ModifyTablePropertiesOp.java
@@ -247,6 +247,23 @@ public class ModifyTablePropertiesOp extends AlterTableOp {
             }
             this.needTableStable = false;
             this.opType = AlterOpType.MODIFY_TABLE_PROPERTY_SYNC;
+        } else if 
(properties.containsKey(PropertyAnalyzer.PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP))
 {
+            int numColumnsPerGroup;
+            String numColumnsPerGroupStr = properties
+                    
.get(PropertyAnalyzer.PROPERTIES_VERTICAL_COMPACTION_NUM_COLUMNS_PER_GROUP);
+            try {
+                numColumnsPerGroup = Integer.parseInt(numColumnsPerGroupStr);
+                if (numColumnsPerGroup < 1 || numColumnsPerGroup > 50) {
+                    throw new AnalysisException(
+                            "vertical_compaction_num_columns_per_group must be 
between 1 and 50: "
+                                    + numColumnsPerGroupStr);
+                }
+            } catch (NumberFormatException e) {
+                throw new AnalysisException("Invalid 
vertical_compaction_num_columns_per_group format: "
+                        + numColumnsPerGroupStr);
+            }
+            this.needTableStable = false;
+            this.opType = AlterOpType.MODIFY_TABLE_PROPERTY_SYNC;
         } else if 
(properties.containsKey(PropertyAnalyzer.PROPERTIES_SKIP_WRITE_INDEX_ON_LOAD)) {
             if 
(properties.get(PropertyAnalyzer.PROPERTIES_SKIP_WRITE_INDEX_ON_LOAD).equalsIgnoreCase("true"))
 {
                 throw new AnalysisException(
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/task/CreateReplicaTask.java 
b/fe/fe-core/src/main/java/org/apache/doris/task/CreateReplicaTask.java
index 0795d66e559..1d03b7bfe9b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/task/CreateReplicaTask.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/task/CreateReplicaTask.java
@@ -125,6 +125,8 @@ public class CreateReplicaTask extends AgentTask {
 
     private long timeSeriesCompactionLevelThreshold;
 
+    private int verticalCompactionNumColumnsPerGroup;
+
     private boolean storeRowColumn;
 
     private BinlogConfig binlogConfig;
@@ -165,7 +167,8 @@ public class CreateReplicaTask extends AgentTask {
                              long rowStorePageSize,
                              boolean variantEnableFlattenNested,
                              long storagePageSize, TEncryptionAlgorithm 
tdeAlgorithm,
-                             long storageDictPageSize, Map<String, 
List<String>> columnSeqMapping) {
+                             long storageDictPageSize, Map<String, 
List<String>> columnSeqMapping,
+                             int verticalCompactionNumColumnsPerGroup) {
         super(null, backendId, TTaskType.CREATE, dbId, tableId, partitionId, 
indexId, tabletId);
 
         this.replicaId = replicaId;
@@ -208,6 +211,7 @@ public class CreateReplicaTask extends AgentTask {
         this.timeSeriesCompactionTimeThresholdSeconds = 
timeSeriesCompactionTimeThresholdSeconds;
         this.timeSeriesCompactionEmptyRowsetsThreshold = 
timeSeriesCompactionEmptyRowsetsThreshold;
         this.timeSeriesCompactionLevelThreshold = 
timeSeriesCompactionLevelThreshold;
+        this.verticalCompactionNumColumnsPerGroup = 
verticalCompactionNumColumnsPerGroup;
         this.storeRowColumn = storeRowColumn;
         this.binlogConfig = binlogConfig;
         this.objectPool = objectPool;
@@ -443,6 +447,7 @@ public class CreateReplicaTask extends AgentTask {
         
createTabletReq.setTimeSeriesCompactionTimeThresholdSeconds(timeSeriesCompactionTimeThresholdSeconds);
         
createTabletReq.setTimeSeriesCompactionEmptyRowsetsThreshold(timeSeriesCompactionEmptyRowsetsThreshold);
         
createTabletReq.setTimeSeriesCompactionLevelThreshold(timeSeriesCompactionLevelThreshold);
+        
createTabletReq.setVerticalCompactionNumColumnsPerGroup(verticalCompactionNumColumnsPerGroup);
         createTabletReq.setTdeAlgorithm(tdeAlgorithm);
 
         if (binlogConfig != null) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/task/UpdateTabletMetaInfoTask.java 
b/fe/fe-core/src/main/java/org/apache/doris/task/UpdateTabletMetaInfoTask.java
index 7d4c6a3d022..4ba127f4472 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/task/UpdateTabletMetaInfoTask.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/task/UpdateTabletMetaInfoTask.java
@@ -54,6 +54,7 @@ public class UpdateTabletMetaInfoTask extends AgentTask {
     private int enableSingleReplicaCompaction = -1;
     private int skipWriteIndexOnLoad = -1;
     private int disableAutoCompaction = -1;
+    private int verticalCompactionNumColumnsPerGroup = -1;
 
     public UpdateTabletMetaInfoTask(long backendId, Set<Pair<Long, Integer>> 
tableIdWithSchemaHash) {
         super(null, backendId, TTaskType.UPDATE_TABLET_META_INFO,
@@ -89,13 +90,15 @@ public class UpdateTabletMetaInfoTask extends AgentTask {
                                     Map<String, Long> 
timeSeriesCompactionConfig,
                                     int enableSingleReplicaCompaction,
                                     int skipWriteIndexOnLoad,
-                                    int disableAutoCompaction) {
+                                    int disableAutoCompaction,
+                                    int verticalCompactionNumColumnsPerGroup) {
         this(backendId, tableIdWithSchemaHash, inMemory, storagePolicyId, 
binlogConfig, latch);
         this.compactionPolicy = compactionPolicy;
         this.timeSeriesCompactionConfig = timeSeriesCompactionConfig;
         this.enableSingleReplicaCompaction = enableSingleReplicaCompaction;
         this.skipWriteIndexOnLoad = skipWriteIndexOnLoad;
         this.disableAutoCompaction = disableAutoCompaction;
+        this.verticalCompactionNumColumnsPerGroup = 
verticalCompactionNumColumnsPerGroup;
     }
 
     public void countDownLatch(long backendId, Set<Pair<Long, Integer>> 
tablets) {
@@ -179,6 +182,9 @@ public class UpdateTabletMetaInfoTask extends AgentTask {
                 if (disableAutoCompaction >= 0) {
                     metaInfo.setDisableAutoCompaction(disableAutoCompaction > 
0);
                 }
+                if (verticalCompactionNumColumnsPerGroup >= 0) {
+                    
metaInfo.setVerticalCompactionNumColumnsPerGroup(verticalCompactionNumColumnsPerGroup);
+                }
                 updateTabletMetaInfoReq.addToTabletMetaInfos(metaInfo);
             }
         } else {
diff --git a/fe/fe-core/src/test/java/org/apache/doris/task/AgentTaskTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/task/AgentTaskTest.java
index 799e665039b..a1cef6161bd 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/task/AgentTaskTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/task/AgentTaskTest.java
@@ -116,7 +116,7 @@ public class AgentTaskTest {
                 indexId1, tabletId1, replicaId1, shortKeyNum, schemaHash1, 
version, KeysType.AGG_KEYS, storageType,
                 TStorageMedium.SSD, columns, null, 0, latch, null, false, 
TTabletType.TABLET_TYPE_DISK, null,
                 TCompressionType.LZ4F, false, "", false, false, false, "", 0, 
0, 0, 0, 0, false, null, null, objectPool, rowStorePageSize, false,
-                storagePageSize, TEncryptionAlgorithm.PLAINTEXT, 
storageDictPageSize, new HashMap<>());
+                storagePageSize, TEncryptionAlgorithm.PLAINTEXT, 
storageDictPageSize, new HashMap<>(), 5);
 
         // drop
         dropTask = new DropReplicaTask(backendId1, tabletId1, replicaId1, 
schemaHash1, false);
diff --git a/gensrc/proto/cloud.proto b/gensrc/proto/cloud.proto
index c779506a012..60596a58828 100644
--- a/gensrc/proto/cloud.proto
+++ b/gensrc/proto/cloud.proto
@@ -663,6 +663,7 @@ message TabletMetaInfoPB { // For update tablet meta
     optional int64 time_series_compaction_level_threshold = 12;
     optional bool disable_auto_compaction = 13;
     optional bool enable_mow_light_delete = 14;
+    optional int32 vertical_compaction_num_columns_per_group = 15;
 }
 
 message TabletCompactionJobPB {
diff --git a/gensrc/proto/olap_file.proto b/gensrc/proto/olap_file.proto
index aadacd37879..0d4359fe162 100644
--- a/gensrc/proto/olap_file.proto
+++ b/gensrc/proto/olap_file.proto
@@ -631,6 +631,7 @@ message TabletMetaPB {
     optional int64 time_series_compaction_empty_rowsets_threshold = 32 
[default = 5];
     optional int64 time_series_compaction_level_threshold = 33 [default = 1];
     optional EncryptionAlgorithmPB encryption_algorithm = 34;
+    optional int32 vertical_compaction_num_columns_per_group = 35 [default = 
5];
 
     // For cloud
     optional int64 index_id = 1000;
@@ -688,6 +689,7 @@ message TabletMetaCloudPB {
     optional int64 time_series_compaction_empty_rowsets_threshold = 37 
[default = 5];
     optional int64 time_series_compaction_level_threshold = 38 [default = 1];
     optional EncryptionAlgorithmPB encryption_algorithm = 39;
+    optional int32 vertical_compaction_num_columns_per_group = 40 [default = 
5];
 
     // Use for selectdb-cloud
     optional string table_name = 101;
diff --git a/gensrc/thrift/AgentService.thrift 
b/gensrc/thrift/AgentService.thrift
index e8044a06c9a..51bd59e7ae7 100644
--- a/gensrc/thrift/AgentService.thrift
+++ b/gensrc/thrift/AgentService.thrift
@@ -229,6 +229,7 @@ struct TCreateTabletReq {
     28: optional TInvertedIndexStorageFormat inverted_index_storage_format = 
TInvertedIndexStorageFormat.DEFAULT // Deprecated
     29: optional Types.TInvertedIndexFileStorageFormat 
inverted_index_file_storage_format = Types.TInvertedIndexFileStorageFormat.V2
     30: optional TEncryptionAlgorithm tde_algorithm
+    31: optional i32 vertical_compaction_num_columns_per_group = 5
 
     // For cloud
     1000: optional bool is_in_memory = false
@@ -543,6 +544,7 @@ struct TTabletMetaInfo {
     16: optional bool disable_auto_compaction
     17: optional i64 time_series_compaction_empty_rowsets_threshold
     18: optional i64 time_series_compaction_level_threshold
+    19: optional i32 vertical_compaction_num_columns_per_group
 }
 
 struct TUpdateTabletMetaInfoReq {
diff --git a/regression-test/data/query_p0/system/test_table_properties.out 
b/regression-test/data/query_p0/system/test_table_properties.out
index a6ff1c341f8..0f8b1a2acaf 100644
--- a/regression-test/data/query_p0/system/test_table_properties.out
+++ b/regression-test/data/query_p0/system/test_table_properties.out
@@ -1,6 +1,6 @@
 -- This file is automatically generated. You should know what you did if you 
want to edit this
 -- !select_check_1 --
-108
+111
 
 -- !select_check_2 --
 internal       test_table_properties_db        duplicate_table _auto_bucket    
false
@@ -38,6 +38,7 @@ internal      test_table_properties_db        duplicate_table 
time_series_compaction_goal_si
 internal       test_table_properties_db        duplicate_table 
time_series_compaction_level_threshold  1
 internal       test_table_properties_db        duplicate_table 
time_series_compaction_time_threshold_seconds   3600
 internal       test_table_properties_db        duplicate_table 
variant_enable_flatten_nested   false
+internal       test_table_properties_db        duplicate_table 
vertical_compaction_num_columns_per_group       5
 internal       test_table_properties_db        listtable       _auto_bucket    
false
 internal       test_table_properties_db        listtable       binlog.enable   
false
 internal       test_table_properties_db        listtable       
binlog.max_bytes        9223372036854775807
@@ -73,6 +74,7 @@ internal      test_table_properties_db        listtable       
time_series_compaction_goal_size_mby
 internal       test_table_properties_db        listtable       
time_series_compaction_level_threshold  1
 internal       test_table_properties_db        listtable       
time_series_compaction_time_threshold_seconds   3600
 internal       test_table_properties_db        listtable       
variant_enable_flatten_nested   false
+internal       test_table_properties_db        listtable       
vertical_compaction_num_columns_per_group       5
 internal       test_table_properties_db        unique_table    _auto_bucket    
false
 internal       test_table_properties_db        unique_table    binlog.enable   
false
 internal       test_table_properties_db        unique_table    
binlog.max_bytes        9223372036854775807
@@ -108,6 +110,7 @@ internal    test_table_properties_db        unique_table    
time_series_compaction_goal_size_
 internal       test_table_properties_db        unique_table    
time_series_compaction_level_threshold  1
 internal       test_table_properties_db        unique_table    
time_series_compaction_time_threshold_seconds   3600
 internal       test_table_properties_db        unique_table    
variant_enable_flatten_nested   false
+internal       test_table_properties_db        unique_table    
vertical_compaction_num_columns_per_group       5
 
 -- !select_check_3 --
 internal       test_table_properties_db        duplicate_table _auto_bucket    
false
@@ -145,6 +148,7 @@ internal    test_table_properties_db        duplicate_table 
time_series_compaction_goal_si
 internal       test_table_properties_db        duplicate_table 
time_series_compaction_level_threshold  1
 internal       test_table_properties_db        duplicate_table 
time_series_compaction_time_threshold_seconds   3600
 internal       test_table_properties_db        duplicate_table 
variant_enable_flatten_nested   false
+internal       test_table_properties_db        duplicate_table 
vertical_compaction_num_columns_per_group       5
 internal       test_table_properties_db        unique_table    _auto_bucket    
false
 internal       test_table_properties_db        unique_table    binlog.enable   
false
 internal       test_table_properties_db        unique_table    
binlog.max_bytes        9223372036854775807
@@ -180,6 +184,7 @@ internal    test_table_properties_db        unique_table    
time_series_compaction_goal_size_
 internal       test_table_properties_db        unique_table    
time_series_compaction_level_threshold  1
 internal       test_table_properties_db        unique_table    
time_series_compaction_time_threshold_seconds   3600
 internal       test_table_properties_db        unique_table    
variant_enable_flatten_nested   false
+internal       test_table_properties_db        unique_table    
vertical_compaction_num_columns_per_group       5
 
 -- !select_check_4 --
 
@@ -219,6 +224,7 @@ internal    test_table_properties_db        duplicate_table 
time_series_compaction_goal_si
 internal       test_table_properties_db        duplicate_table 
time_series_compaction_level_threshold  1
 internal       test_table_properties_db        duplicate_table 
time_series_compaction_time_threshold_seconds   3600
 internal       test_table_properties_db        duplicate_table 
variant_enable_flatten_nested   false
+internal       test_table_properties_db        duplicate_table 
vertical_compaction_num_columns_per_group       5
 
 -- !select_check_6 --
 
diff --git 
a/regression-test/data/variant_p0/test_variant_modify_doc_materialization_min_rows.out
 
b/regression-test/data/variant_p0/test_variant_modify_doc_materialization_min_rows.out
new file mode 100644
index 00000000000..56a06bcf8fa
--- /dev/null
+++ 
b/regression-test/data/variant_p0/test_variant_modify_doc_materialization_min_rows.out
@@ -0,0 +1,22 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !desc_step1 --
+k      int     Yes     true    \N      
+v      variant<PROPERTIES ("variant_enable_doc_mode" = 
"true","variant_doc_materialization_min_rows" = 
"10","variant_doc_hash_shard_count" = "32")>    Yes     false   \N      NONE
+
+-- !desc_step2 --
+k      int     Yes     true    \N      
+v      variant<PROPERTIES ("variant_enable_doc_mode" = 
"true","variant_doc_materialization_min_rows" = 
"2","variant_doc_hash_shard_count" = "32")>     Yes     false   \N      NONE
+v.path_d       bigint  Yes     false   \N      NONE
+v.path_e       text    Yes     false   \N      NONE
+v.path_f       double  Yes     false   \N      NONE
+
+-- !desc_step3 --
+k      int     Yes     true    \N      
+v      variant<PROPERTIES ("variant_enable_doc_mode" = 
"true","variant_doc_materialization_min_rows" = 
"2","variant_doc_hash_shard_count" = "32")>     Yes     false   \N      NONE
+v.path_a       bigint  Yes     false   \N      NONE
+v.path_b       text    Yes     false   \N      NONE
+v.path_c       double  Yes     false   \N      NONE
+v.path_d       bigint  Yes     false   \N      NONE
+v.path_e       text    Yes     false   \N      NONE
+v.path_f       double  Yes     false   \N      NONE
+
diff --git 
a/regression-test/suites/compaction/test_vertical_compaction_num_columns_per_group.groovy
 
b/regression-test/suites/compaction/test_vertical_compaction_num_columns_per_group.groovy
new file mode 100644
index 00000000000..129cd6d0309
--- /dev/null
+++ 
b/regression-test/suites/compaction/test_vertical_compaction_num_columns_per_group.groovy
@@ -0,0 +1,156 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_vertical_compaction_num_columns_per_group", "nonConcurrent") {
+    def tableName = "test_columns_per_group"
+
+    // Test 1: Create table with property set to 2
+    sql """ DROP TABLE IF EXISTS ${tableName}_2 """
+    sql """ CREATE TABLE ${tableName}_2 (
+        k1 INT,
+        v1 INT, v2 INT, v3 INT, v4 INT, v5 INT,
+        v6 INT, v7 INT, v8 INT, v9 INT, v10 INT
+    ) DUPLICATE KEY(k1)
+    DISTRIBUTED BY HASH(k1) BUCKETS 1
+    PROPERTIES (
+        "replication_num" = "1",
+        "vertical_compaction_num_columns_per_group" = "2",
+        "disable_auto_compaction" = "true"
+    ); """
+
+    // Get tablet info
+    def tablets = sql_return_maparray """ SHOW TABLETS FROM ${tableName}_2; """
+    def tablet_id = tablets[0].TabletId
+    logger.info("Test 1 - tablet_id: ${tablet_id}")
+
+    // Verify SHOW CREATE TABLE contains property
+    def createTableResult = sql """ SHOW CREATE TABLE ${tableName}_2; """
+    def createTableStr = createTableResult[0][1]
+    logger.info("Test 1 - SHOW CREATE TABLE: ${createTableStr}")
+    
assertTrue(createTableStr.contains('"vertical_compaction_num_columns_per_group" 
= "2"'),
+        "SHOW CREATE TABLE should contain 
vertical_compaction_num_columns_per_group=2 after CREATE")
+
+    // Enable debug point with tablet_id matching - if value doesn't match, BE 
will LOG(FATAL)
+    try {
+        GetDebugPoint().enableDebugPointForAllBEs(
+            "Merger.vertical_merge_rowsets.check_num_columns_per_group",
+            [expected_value: "2", tablet_id: "${tablet_id}"])
+
+        // Insert data to trigger compaction
+        for (int i = 0; i < 5; i++) {
+            sql """ INSERT INTO ${tableName}_2 VALUES
+                    (${i}, ${i}, ${i}, ${i}, ${i}, ${i},
+                     ${i}, ${i}, ${i}, ${i}, ${i}); """
+        }
+
+        // Trigger and wait for compaction
+        trigger_and_wait_compaction("${tableName}_2", "full")
+        logger.info("Test 1 - Compaction finished, value 2 verified for tablet 
${tablet_id}")
+
+    } finally {
+        GetDebugPoint().disableDebugPointForAllBEs(
+            "Merger.vertical_merge_rowsets.check_num_columns_per_group")
+    }
+
+    // Test 2: Create table without setting property (should use default value 
5)
+    sql """ DROP TABLE IF EXISTS ${tableName}_default """
+    sql """ CREATE TABLE ${tableName}_default (
+        k1 INT,
+        v1 INT, v2 INT, v3 INT, v4 INT, v5 INT
+    ) DUPLICATE KEY(k1)
+    DISTRIBUTED BY HASH(k1) BUCKETS 1
+    PROPERTIES (
+        "replication_num" = "1",
+        "disable_auto_compaction" = "true"
+    ); """
+
+    def tablets_default = sql_return_maparray """ SHOW TABLETS FROM 
${tableName}_default; """
+    def tablet_id_default = tablets_default[0].TabletId
+    logger.info("Test 2 - tablet_id_default: ${tablet_id_default}")
+
+    try {
+        // Verify default value is 5
+        GetDebugPoint().enableDebugPointForAllBEs(
+            "Merger.vertical_merge_rowsets.check_num_columns_per_group",
+            [expected_value: "5", tablet_id: "${tablet_id_default}"])
+
+        // Insert data
+        for (int i = 0; i < 5; i++) {
+            sql """ INSERT INTO ${tableName}_default VALUES (${i}, ${i}, ${i}, 
${i}, ${i}, ${i}); """
+        }
+
+        // Trigger and wait for compaction
+        trigger_and_wait_compaction("${tableName}_default", "full")
+        logger.info("Test 2 - Compaction finished, default value 5 verified 
for tablet ${tablet_id_default}")
+
+    } finally {
+        GetDebugPoint().disableDebugPointForAllBEs(
+            "Merger.vertical_merge_rowsets.check_num_columns_per_group")
+    }
+
+    // Test 3: ALTER TABLE to modify property from 2 to 10
+    sql """ ALTER TABLE ${tableName}_2 SET 
("vertical_compaction_num_columns_per_group" = "10"); """
+    logger.info("Test 3 - ALTER TABLE executed, changed value from 2 to 10")
+    sql """sync"""
+    
+    Thread.sleep(1000)
+    // Verify SHOW CREATE TABLE reflects the change
+    def createTableResult3 = sql """ SHOW CREATE TABLE ${tableName}_2; """
+    def createTableStr3 = createTableResult3[0][1]
+    logger.info("Test 3 - SHOW CREATE TABLE after ALTER: ${createTableStr3}")
+    
assertTrue(createTableStr3.contains('"vertical_compaction_num_columns_per_group"
 = "10"'),
+        "SHOW CREATE TABLE should contain 
vertical_compaction_num_columns_per_group=10 after ALTER")
+
+
+    // Wait for ALTER TABLE to take effect
+    // In cloud mode, BE syncs from MS which may take longer
+    if (isCloudMode()) {
+        return
+    }
+    try {
+        // Verify modified value is 10
+        GetDebugPoint().enableDebugPointForAllBEs(
+            "Merger.vertical_merge_rowsets.check_num_columns_per_group",
+            [expected_value: "10", tablet_id: "${tablet_id}"])
+
+        // Insert more data
+        for (int i = 5; i < 10; i++) {
+            sql """ INSERT INTO ${tableName}_2 VALUES
+                    (${i}, ${i}, ${i}, ${i}, ${i}, ${i},
+                     ${i}, ${i}, ${i}, ${i}, ${i}); """
+        }
+
+        // Trigger and wait for compaction
+        trigger_and_wait_compaction("${tableName}_2", "full")
+        logger.info("Test 3 - Compaction finished, altered value 10 verified 
for tablet ${tablet_id}")
+
+    } finally {
+        GetDebugPoint().disableDebugPointForAllBEs(
+            "Merger.vertical_merge_rowsets.check_num_columns_per_group")
+    }
+
+    // Verify data correctness
+    def result = sql """ SELECT COUNT(*) FROM ${tableName}_2; """
+    assertEquals(10, result[0][0])
+
+    // Check tablet meta shows correct value
+    def tabletMeta = sql_return_maparray """ SHOW TABLETS FROM ${tableName}_2; 
"""
+    logger.info("Test 3 - tablet meta after ALTER: ${tabletMeta[0]}")
+
+    logger.info("=== All tests passed ===")
+    logger.info("If any debug point check failed, the BE would have crashed 
with LOG(FATAL)")
+}
diff --git 
a/regression-test/suites/variant_p0/test_variant_modify_doc_materialization_min_rows.groovy
 
b/regression-test/suites/variant_p0/test_variant_modify_doc_materialization_min_rows.groovy
new file mode 100644
index 00000000000..4854ec8b8e6
--- /dev/null
+++ 
b/regression-test/suites/variant_p0/test_variant_modify_doc_materialization_min_rows.groovy
@@ -0,0 +1,98 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_variant_modify_doc_materialization_min_rows", "p0") {
+    def tableName = "test_variant_modify_doc_min_rows"
+
+    sql """ DROP TABLE IF EXISTS ${tableName} """
+
+    // Step 1: Create table with variant_doc_materialization_min_rows=10
+    sql """
+        CREATE TABLE ${tableName} (
+            k INT,
+            v 
variant<properties("variant_enable_doc_mode"="true","variant_doc_materialization_min_rows"="10",
 "variant_doc_hash_shard_count" = "32")>
+        ) ENGINE=OLAP
+        DUPLICATE KEY(k)
+        DISTRIBUTED BY HASH(k) BUCKETS 1
+        PROPERTIES (
+            "replication_num" = "1",
+            "disable_auto_compaction" = "true"
+        );
+    """
+
+    // Verify SHOW CREATE TABLE shows min_rows=5
+    def createResult = sql """ SHOW CREATE TABLE ${tableName} """
+    def createStmt = createResult[0][1]
+    assertTrue(createStmt.contains('"variant_doc_materialization_min_rows" = 
"10"'),
+        "SHOW CREATE TABLE should show 
variant_doc_materialization_min_rows=10, got: ${createStmt}")
+
+    // Verify tablet meta shows min_rows=5
+    // Insert 3 rows with different paths (3 < 5, so no sub-columns should be 
materialized)
+    sql """ INSERT INTO ${tableName} VALUES
+        (1, '{"path_a": 100, "path_b": "hello", "path_c": 1.5}'),
+        (2, '{"path_a": 200, "path_b": "world", "path_c": 2.5}'),
+        (3, '{"path_a": 300, "path_b": "doris", "path_c": 3.5}')
+    """
+
+    // Sync rowsets
+    sql "SELECT * FROM ${tableName} LIMIT 1"
+
+    // Enable describe_extend_variant_column and check DESC
+    sql """set describe_extend_variant_column = true"""
+    // Expect: only k and v columns, NO sub-columns extracted (3 rows < 
min_rows=5)
+    qt_desc_step1 """desc ${tableName}"""
+
+    // Step 2: ALTER TABLE MODIFY COLUMN to change 
variant_doc_materialization_min_rows to 2
+    sql """
+        ALTER TABLE ${tableName} MODIFY COLUMN v 
variant<properties("variant_enable_doc_mode"="true","variant_doc_materialization_min_rows"="2",
 "variant_doc_hash_shard_count" = "32")>;
+    """
+
+    // Verify SHOW CREATE TABLE shows min_rows=2
+    createResult = sql """ SHOW CREATE TABLE ${tableName} """
+    createStmt = createResult[0][1]
+    assertTrue(createStmt.contains('"variant_doc_materialization_min_rows" = 
"2"'),
+        "SHOW CREATE TABLE should show variant_doc_materialization_min_rows=2 
after ALTER, got: ${createStmt}")
+
+    // Insert 3 more rows with different paths (3 >= 2, so sub-columns should 
be materialized for new data)
+    sql """ INSERT INTO ${tableName} VALUES
+        (4, '{"path_d": 400, "path_e": "alpha", "path_f": 4.5}'),
+        (5, '{"path_d": 500, "path_e": "beta", "path_f": 5.5}'),
+        (6, '{"path_d": 600, "path_e": "gamma", "path_f": 6.5}')
+    """
+
+    // Sync rowsets
+    sql "SELECT * FROM ${tableName} LIMIT 1"
+
+    // Expect: 3 sub-columns from new data (path_d, path_e, path_f)
+    qt_desc_step2 """desc ${tableName}"""
+
+    // Step 3: Trigger compaction - old data (path_a, path_b, path_c) should 
also get materialized
+    trigger_and_wait_compaction(tableName, "full")
+
+    // Sync rowsets
+    sql "SELECT * FROM ${tableName} LIMIT 1"
+
+    // Expect: 6 sub-columns total (path_a through path_f all materialized 
after compaction with min_rows=2)
+    qt_desc_step3 """desc ${tableName}"""
+
+    // Verify data integrity
+    def count = sql """ SELECT COUNT(*) FROM ${tableName} """
+    assertEquals(6, count[0][0])
+
+    // Cleanup
+    sql """ DROP TABLE IF EXISTS ${tableName} """
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to