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

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


The following commit(s) were added to refs/heads/master by this push:
     new dd36ccc  [feature](storage-format) Z-Order Implement (#7149)
dd36ccc is described below

commit dd36ccc3bf2f04d01cb6a4f5014fa605a412786a
Author: xinghuayu007 <[email protected]>
AuthorDate: Thu Dec 2 11:39:51 2021 +0800

    [feature](storage-format) Z-Order Implement (#7149)
    
    Support sort data by Z-Order:
    
    ```
    CREATE TABLE table2 (
    siteid int(11) NULL DEFAULT "10" COMMENT "",
    citycode int(11) NULL COMMENT "",
    username varchar(32) NULL DEFAULT "" COMMENT "",
    pv bigint(20) NULL DEFAULT "0" COMMENT ""
    ) ENGINE=OLAP
    DUPLICATE KEY(siteid, citycode)
    COMMENT "OLAP"
    DISTRIBUTED BY HASH(siteid) BUCKETS 1
    PROPERTIES (
    "replication_allocation" = "tag.location.default: 1",
    "data_sort.sort_type" = "ZORDER",
    "data_sort.col_num" = "2",
    "in_memory" = "false",
    "storage_format" = "V2"
    );
    ```
---
 be/src/olap/collect_iterator.cpp                   |   55 +-
 be/src/olap/collect_iterator.h                     |   35 +-
 be/src/olap/memtable.cpp                           |   13 +-
 be/src/olap/memtable.h                             |   10 +-
 be/src/olap/rowset/segment_v2/column_reader.cpp    |    2 +
 be/src/olap/rowset/segment_v2/segment_iterator.cpp |    5 +-
 be/src/olap/skiplist.h                             |   14 +-
 be/src/olap/tablet.h                               |   10 +
 be/src/olap/tablet_meta.cpp                        |    9 +
 be/src/olap/tablet_schema.cpp                      |    4 +
 be/src/olap/tablet_schema.h                        |    4 +
 be/src/util/CMakeLists.txt                         |    1 +
 be/src/util/bit_util.h                             |  116 ++
 be/src/util/tuple_row_zorder_compare.cpp           |  233 ++++
 be/src/util/tuple_row_zorder_compare.h             |   70 ++
 be/test/olap/skiplist_test.cpp                     |   16 +-
 be/test/olap/test_data/header_without_inc_rs.txt   |    4 +-
 be/test/util/CMakeLists.txt                        |    1 +
 be/test/util/tuple_row_zorder_compare_test.cpp     | 1120 ++++++++++++++++++++
 .../Create/CREATE-TABLE.md                         |    7 +
 .../Create/CREATE-TABLE.md                         |    6 +
 .../main/java/org/apache/doris/alter/Alter.java    |    5 +
 .../org/apache/doris/analysis/DataSortInfo.java    |  103 ++
 .../java/org/apache/doris/catalog/Catalog.java     |   46 +-
 .../java/org/apache/doris/catalog/OlapTable.java   |   24 +-
 .../org/apache/doris/catalog/TableProperty.java    |   26 +-
 .../apache/doris/common/util/PropertyAnalyzer.java |   41 +
 .../org/apache/doris/task/CreateReplicaTask.java   |   41 +
 .../org/apache/doris/catalog/CreateTableTest.java  |   41 +
 gensrc/proto/olap_file.proto                       |    7 +
 gensrc/thrift/AgentService.thrift                  |    2 +
 gensrc/thrift/Types.thrift                         |    5 +
 32 files changed, 2025 insertions(+), 51 deletions(-)

diff --git a/be/src/olap/collect_iterator.cpp b/be/src/olap/collect_iterator.cpp
index 3f4d8b5..22446a0 100644
--- a/be/src/olap/collect_iterator.cpp
+++ b/be/src/olap/collect_iterator.cpp
@@ -56,6 +56,8 @@ OLAPStatus CollectIterator::add_child(RowsetReaderSharedPtr 
rs_reader) {
 void CollectIterator::build_heap(const std::vector<RowsetReaderSharedPtr>& 
rs_readers) {
     DCHECK(rs_readers.size() == _children.size());
     _reverse = _reader->_tablet->tablet_schema().keys_type() == 
KeysType::UNIQUE_KEYS;
+    SortType sort_type = _reader->_tablet->tablet_schema().sort_type();
+    int sort_col_num = _reader->_tablet->tablet_schema().sort_col_num();
     if (_children.empty()) {
         _inner_iter.reset(nullptr);
         return;
@@ -86,18 +88,19 @@ void CollectIterator::build_heap(const 
std::vector<RowsetReaderSharedPtr>& rs_re
                 ++i;
             }
             Level1Iterator* cumu_iter =
-                    new Level1Iterator(cumu_children, cumu_children.size() > 
1, _reverse, _reader->_sequence_col_idx);
+                    new Level1Iterator(cumu_children, cumu_children.size() > 
1, _reverse,
+                                       _reader->_sequence_col_idx, sort_type, 
sort_col_num);
             cumu_iter->init();
             _inner_iter.reset(new 
Level1Iterator(std::list<LevelIterator*>{*base_reader_child, cumu_iter}, _merge,
-                    _reverse, _reader->_sequence_col_idx));
+                    _reverse, _reader->_sequence_col_idx, sort_type, 
sort_col_num));
         } else {
             // _children.size() == 1
             _inner_iter.reset(new Level1Iterator(_children, _merge,
-                    _reverse, _reader->_sequence_col_idx));
+                    _reverse, _reader->_sequence_col_idx, sort_type, 
sort_col_num));
         }
     } else {
         _inner_iter.reset(new Level1Iterator(_children, _merge,
-                _reverse, _reader->_sequence_col_idx));
+                _reverse, _reader->_sequence_col_idx, sort_type, 
sort_col_num));
     }
     _inner_iter->init();
     // Clear _children earlier to release any related references
@@ -132,6 +135,34 @@ bool 
CollectIterator::LevelIteratorComparator::operator()(const LevelIterator* a
     return a->version() > b->version();
 }
 
+CollectIterator::BaseComparator::BaseComparator(
+        std::shared_ptr<LevelIteratorComparator>& cmp) {
+    _cmp = cmp;
+}
+
+bool CollectIterator::BaseComparator::operator()(const LevelIterator* a, const 
LevelIterator* b) {
+    return _cmp->operator()(a, b);
+}
+
+bool CollectIterator::LevelZorderIteratorComparator::operator()(const 
LevelIterator* a,
+                                                                    const 
LevelIterator* b) {
+    // First compare row cursor.
+    const RowCursor* first = a->current_row();
+    const RowCursor* second = b->current_row();
+    int cmp_res = _comparator.compare_row(*first, *second);
+    if (cmp_res != 0) {
+        return cmp_res > 0;
+    }
+    // if row cursors equal, compare data version.
+    // read data from higher version to lower version.
+    // for UNIQUE_KEYS just read the highest version and no need agg_update.
+    // for AGG_KEYS if a version is deleted, the lower version no need to 
agg_update
+    if (_reverse) {
+        return a->version() < b->version();
+    }
+    return a->version() > b->version();
+}
+
 const RowCursor* CollectIterator::current_row(bool* delete_flag) const {
     if (LIKELY(_inner_iter)) {
         return _inner_iter->current_row(delete_flag);
@@ -233,8 +264,11 @@ OLAPStatus CollectIterator::Level0Iterator::next(const 
RowCursor** row, bool* de
 }
 
 CollectIterator::Level1Iterator::Level1Iterator(
-        std::list<CollectIterator::LevelIterator*> children, bool merge, bool 
reverse, int sequence_id_idx)
-        : _children(std::move(children)), _merge(merge), _reverse(reverse), 
_sequence_id_idx(sequence_id_idx) {}
+        const std::list<CollectIterator::LevelIterator*>& children,
+        bool merge, bool reverse, int sequence_id_idx,
+        SortType sort_type, int sort_col_num)
+        : _children(children), _merge(merge), _reverse(reverse),
+        _sort_type(sort_type), _sort_col_num(sort_col_num) {}
 
 CollectIterator::LevelIterator::~LevelIterator() = default;
 
@@ -304,7 +338,14 @@ OLAPStatus CollectIterator::Level1Iterator::init() {
 
     // Only when there are multiple children that need to be merged
     if (_merge && _children.size() > 1) {
-        _heap.reset(new MergeHeap(LevelIteratorComparator(_reverse, 
_sequence_id_idx)));
+        std::shared_ptr<LevelIteratorComparator> cmp;
+        if (_sort_type == SortType::ZORDER) {
+            cmp = std::make_shared<LevelZorderIteratorComparator>(_reverse, 
_sequence_id_idx,  _sort_col_num);
+        } else {
+            cmp = std::make_shared<LevelIteratorComparator>(_reverse, 
_sequence_id_idx);
+        }
+        BaseComparator bcmp(cmp);
+        _heap.reset(new MergeHeap(bcmp));
         for (auto child : _children) {
             DCHECK(child != nullptr);
             DCHECK(child->current_row() != nullptr);
diff --git a/be/src/olap/collect_iterator.h b/be/src/olap/collect_iterator.h
index 9965ae2..60b670e 100644
--- a/be/src/olap/collect_iterator.h
+++ b/be/src/olap/collect_iterator.h
@@ -20,6 +20,7 @@
 #include "olap/olap_define.h"
 #include "olap/row_cursor.h"
 #include "olap/rowset/rowset_reader.h"
+#include "util/tuple_row_zorder_compare.h"
 
 namespace doris {
 
@@ -74,15 +75,39 @@ private:
     public:
         LevelIteratorComparator(const bool reverse = false, int 
sequence_id_idx = -1) :
             _reverse(reverse), _sequence_id_idx(sequence_id_idx) {}
-        bool operator()(const LevelIterator* a, const LevelIterator* b);
+        virtual bool operator()(const LevelIterator* a, const LevelIterator* 
b);
 
     private:
         bool _reverse;
         int _sequence_id_idx;
     };
 
+    class LevelZorderIteratorComparator: public LevelIteratorComparator {
+    public:
+        LevelZorderIteratorComparator(const bool reverse = false, int 
sequence_id_idx = -1, const size_t sort_col_num = 0) :
+            _reverse(reverse), _sequence_id_idx(sequence_id_idx), 
_sort_col_num(sort_col_num) {
+            _comparator = TupleRowZOrderComparator(sort_col_num);
+        }
+        virtual bool operator()(const LevelIterator* a, const LevelIterator* 
b);
+
+    private:
+        bool _reverse = false;
+        int _sequence_id_idx;
+        size_t _sort_col_num = 0;
+        TupleRowZOrderComparator _comparator;
+    };
+
+    class BaseComparator {
+    public:
+        BaseComparator(std::shared_ptr<LevelIteratorComparator>& cmp);
+        bool operator()(const LevelIterator* a, const LevelIterator* b);
+
+    private:
+        std::shared_ptr<LevelIteratorComparator> _cmp;
+    };
+
     typedef std::priority_queue<LevelIterator*, std::vector<LevelIterator*>,
-                                LevelIteratorComparator>
+                    BaseComparator>
             MergeHeap;
     // Iterate from rowset reader. This Iterator usually like a leaf node
     class Level0Iterator : public LevelIterator {
@@ -118,7 +143,9 @@ private:
     // Iterate from LevelIterators (maybe Level0Iterators or Level1Iterator or 
mixed)
     class Level1Iterator : public LevelIterator {
     public:
-        Level1Iterator(std::list<LevelIterator*>, bool, bool, int);
+
+        Level1Iterator(const std::list<LevelIterator*>& children, bool merge, 
bool reverse,
+                       int sequence_id_idx, SortType sort_type, int 
sort_col_num);
 
         OLAPStatus init() override;
 
@@ -157,6 +184,8 @@ private:
         // used when `_merge == false`
         int _child_idx = 0;
         int _sequence_id_idx = -1;
+        SortType _sort_type;
+        int _sort_col_num;
     };
 
     std::unique_ptr<LevelIterator> _inner_iter;
diff --git a/be/src/olap/memtable.cpp b/be/src/olap/memtable.cpp
index 3ce5db1..2ec1ccb 100644
--- a/be/src/olap/memtable.cpp
+++ b/be/src/olap/memtable.cpp
@@ -39,14 +39,19 @@ MemTable::MemTable(int64_t tablet_id, Schema* schema, const 
TabletSchema* tablet
           _tuple_desc(tuple_desc),
           _slot_descs(slot_descs),
           _keys_type(keys_type),
-          _row_comparator(_schema),
           _mem_tracker(MemTracker::CreateTracker(-1, "MemTable", 
parent_tracker)),
           _buffer_mem_pool(new MemPool(_mem_tracker.get())),
           _table_mem_pool(new MemPool(_mem_tracker.get())),
           _schema_size(_schema->schema_size()),
-          _skip_list(new Table(_row_comparator, _table_mem_pool.get(),
-                               _keys_type == KeysType::DUP_KEYS)),
-          _rowset_writer(rowset_writer) {}
+          _rowset_writer(rowset_writer) {
+    if (tablet_schema->sort_type() == SortType::ZORDER) {
+        _row_comparator = std::make_shared<TupleRowZOrderComparator>(_schema, 
tablet_schema->sort_col_num());
+    } else {
+        _row_comparator = std::make_shared<RowCursorComparator>(_schema);
+    }
+    _skip_list = new Table(_row_comparator.get(), _table_mem_pool.get(), 
_keys_type == KeysType::DUP_KEYS);
+
+}
 
 MemTable::~MemTable() {
     delete _skip_list;
diff --git a/be/src/olap/memtable.h b/be/src/olap/memtable.h
index 02783d7..fdb574c 100644
--- a/be/src/olap/memtable.h
+++ b/be/src/olap/memtable.h
@@ -24,6 +24,7 @@
 #include "olap/olap_define.h"
 #include "olap/skiplist.h"
 #include "runtime/mem_tracker.h"
+#include "util/tuple_row_zorder_compare.h"
 
 namespace doris {
 
@@ -53,16 +54,17 @@ public:
     int64_t flush_size() const { return _flush_size; }
 
 private:
-    class RowCursorComparator {
+    class RowCursorComparator: public RowComparator {
     public:
         RowCursorComparator(const Schema* schema);
-        int operator()(const char* left, const char* right) const;
+        virtual int operator()(const char* left, const char* right) const;
 
     private:
         const Schema* _schema;
     };
 
-    typedef SkipList<char*, RowCursorComparator> Table;
+private:
+    typedef SkipList<char*, RowComparator> Table;
     typedef Table::key_type TableKey;
 
 public:
@@ -95,7 +97,7 @@ private:
     const std::vector<SlotDescriptor*>* _slot_descs;
     KeysType _keys_type;
 
-    RowCursorComparator _row_comparator;
+    std::shared_ptr<RowComparator> _row_comparator;
     std::shared_ptr<MemTracker> _mem_tracker;
     // This is a buffer, to hold the memory referenced by the rows that have 
not
     // been inserted into the SkipList
diff --git a/be/src/olap/rowset/segment_v2/column_reader.cpp 
b/be/src/olap/rowset/segment_v2/column_reader.cpp
index 7ac50a7..3d78ceb 100644
--- a/be/src/olap/rowset/segment_v2/column_reader.cpp
+++ b/be/src/olap/rowset/segment_v2/column_reader.cpp
@@ -239,6 +239,8 @@ Status ColumnReader::_get_filtered_pages(CondColumn* 
cond_column, CondColumn* de
             }
         }
     }
+    VLOG(1) << "total-pages: " << page_size << " not-filtered-pages: " << 
page_indexes->size()
+                << " filtered-percent:" << 1.0 - 
(page_indexes->size()*1.0)/(page_size*1.0);
     return Status::OK();
 }
 
diff --git a/be/src/olap/rowset/segment_v2/segment_iterator.cpp 
b/be/src/olap/rowset/segment_v2/segment_iterator.cpp
index 24b7d74..6406d95 100644
--- a/be/src/olap/rowset/segment_v2/segment_iterator.cpp
+++ b/be/src/olap/rowset/segment_v2/segment_iterator.cpp
@@ -128,7 +128,10 @@ Status SegmentIterator::_init() {
     _row_bitmap.addRange(0, _segment->num_rows());
     RETURN_IF_ERROR(_init_return_column_iterators());
     RETURN_IF_ERROR(_init_bitmap_index_iterators());
-    RETURN_IF_ERROR(_get_row_ranges_by_keys());
+    // z-order can not use prefix index
+    if (_segment->_tablet_schema->sort_type() != SortType::ZORDER) {
+        RETURN_IF_ERROR(_get_row_ranges_by_keys());
+    }
     RETURN_IF_ERROR(_get_row_ranges_by_column_conditions());
     _init_lazy_materialization();
     _range_iter.reset(new BitmapRangeIterator(_row_bitmap));
diff --git a/be/src/olap/skiplist.h b/be/src/olap/skiplist.h
index f2676de..8d392b4 100644
--- a/be/src/olap/skiplist.h
+++ b/be/src/olap/skiplist.h
@@ -67,7 +67,7 @@ public:
     // and will allocate memory using "*mem_pool".
     // NOTE: Objects allocated in the mem_pool must remain allocated for
     // the lifetime of the skiplist object.
-    explicit SkipList(Comparator cmp, MemPool* mem_pool, bool can_dup);
+    explicit SkipList(Comparator* cmp, MemPool* mem_pool, bool can_dup);
 
     // Insert key into the list.
     void Insert(const Key& key, bool* overwritten);
@@ -121,7 +121,7 @@ public:
 
 private:
     // Immutable after construction
-    Comparator const compare_;
+    Comparator* const compare_;
     // When value is true, means indicates that duplicate values are allowed.
     bool _can_dup;
     MemPool* const _mem_pool; // MemPool used for allocations of nodes
@@ -139,7 +139,7 @@ private:
 
     Node* NewNode(const Key& key, int height);
     int RandomHeight();
-    bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 
0); }
+    bool Equal(const Key& a, const Key& b) const { return ((*compare_)(a, b) 
== 0); }
 
     // Return true if key is greater than the data stored in "n"
     bool KeyIsAfterNode(const Key& key, Node* n) const;
@@ -277,7 +277,7 @@ int SkipList<Key, Comparator>::RandomHeight() {
 template <typename Key, class Comparator>
 bool SkipList<Key, Comparator>::KeyIsAfterNode(const Key& key, Node* n) const {
     // nullptr n is considered infinite
-    return (n != nullptr) && (compare_(n->key, key) < 0);
+    return (n != nullptr) && ((*compare_)(n->key, key) < 0);
 }
 
 template <typename Key, class Comparator>
@@ -308,9 +308,9 @@ typename SkipList<Key, Comparator>::Node* SkipList<Key, 
Comparator>::FindLessTha
     Node* x = head_;
     int level = GetMaxHeight() - 1;
     while (true) {
-        DCHECK(x == head_ || compare_(x->key, key) < 0);
+        DCHECK(x == head_ || (*compare_)(x->key, key) < 0);
         Node* next = x->Next(level);
-        if (next == nullptr || compare_(next->key, key) >= 0) {
+        if (next == nullptr || (*compare_)(next->key, key) >= 0) {
             if (level == 0) {
                 return x;
             } else {
@@ -343,7 +343,7 @@ typename SkipList<Key, Comparator>::Node* SkipList<Key, 
Comparator>::FindLast()
 }
 
 template <typename Key, class Comparator>
-SkipList<Key, Comparator>::SkipList(Comparator cmp, MemPool* mem_pool, bool 
can_dup)
+SkipList<Key, Comparator>::SkipList(Comparator* cmp, MemPool* mem_pool, bool 
can_dup)
         : compare_(cmp),
           _can_dup(can_dup),
           _mem_pool(mem_pool),
diff --git a/be/src/olap/tablet.h b/be/src/olap/tablet.h
index 437c136..c93c3b2 100644
--- a/be/src/olap/tablet.h
+++ b/be/src/olap/tablet.h
@@ -82,6 +82,8 @@ public:
 
     // properties encapsulated in TabletSchema
     inline KeysType keys_type() const;
+    inline SortType sort_type() const;
+    inline size_t sort_col_num() const;
     inline size_t num_columns() const;
     inline size_t num_null_columns() const;
     inline size_t num_key_columns() const;
@@ -394,6 +396,14 @@ inline KeysType Tablet::keys_type() const {
     return _schema.keys_type();
 }
 
+inline SortType Tablet::sort_type() const {
+    return _schema.sort_type();
+}
+
+inline size_t Tablet::sort_col_num() const {
+    return _schema.sort_col_num();
+}
+
 inline size_t Tablet::num_columns() const {
     return _schema.num_columns();
 }
diff --git a/be/src/olap/tablet_meta.cpp b/be/src/olap/tablet_meta.cpp
index 71794ed..8739486 100644
--- a/be/src/olap/tablet_meta.cpp
+++ b/be/src/olap/tablet_meta.cpp
@@ -86,6 +86,15 @@ TabletMeta::TabletMeta(int64_t table_id, int64_t 
partition_id, int64_t tablet_id
         break;
     }
     schema->set_compress_kind(COMPRESS_LZ4);
+
+    switch(tablet_schema.sort_type) {
+        case TSortType::type::ZORDER:
+            schema->set_sort_type(SortType::ZORDER);
+            break;
+        default:
+            schema->set_sort_type(SortType::LEXICAL);
+    }
+    schema->set_sort_col_num(tablet_schema.sort_col_num);
     tablet_meta_pb.set_in_restore_mode(false);
 
     // set column information
diff --git a/be/src/olap/tablet_schema.cpp b/be/src/olap/tablet_schema.cpp
index dd29b73..072a415 100644
--- a/be/src/olap/tablet_schema.cpp
+++ b/be/src/olap/tablet_schema.cpp
@@ -422,6 +422,8 @@ void TabletSchema::init_from_pb(const TabletSchemaPB& 
schema) {
     _is_in_memory = schema.is_in_memory();
     _delete_sign_idx = schema.delete_sign_idx();
     _sequence_col_idx = schema.sequence_col_idx();
+    _sort_type = schema.sort_type();
+    _sort_col_num = schema.sort_col_num();
 }
 
 void TabletSchema::to_schema_pb(TabletSchemaPB* tablet_meta_pb) {
@@ -440,6 +442,8 @@ void TabletSchema::to_schema_pb(TabletSchemaPB* 
tablet_meta_pb) {
     tablet_meta_pb->set_is_in_memory(_is_in_memory);
     tablet_meta_pb->set_delete_sign_idx(_delete_sign_idx);
     tablet_meta_pb->set_sequence_col_idx(_sequence_col_idx);
+    tablet_meta_pb->set_sort_type(_sort_type);
+    tablet_meta_pb->set_sort_col_num(_sort_col_num);
 }
 
 uint32_t TabletSchema::mem_size() const {
diff --git a/be/src/olap/tablet_schema.h b/be/src/olap/tablet_schema.h
index ee2f920..0cb894f 100644
--- a/be/src/olap/tablet_schema.h
+++ b/be/src/olap/tablet_schema.h
@@ -130,6 +130,8 @@ public:
     inline size_t num_short_key_columns() const { return 
_num_short_key_columns; }
     inline size_t num_rows_per_row_block() const { return 
_num_rows_per_row_block; }
     inline KeysType keys_type() const { return _keys_type; }
+    inline SortType sort_type() const { return _sort_type; }
+    inline size_t sort_col_num() const { return _sort_col_num; }
     inline CompressKind compress_kind() const { return _compress_kind; }
     inline size_t next_column_unique_id() const { return 
_next_column_unique_id; }
     inline double bloom_filter_fpp() const { return _bf_fpp; }
@@ -149,6 +151,8 @@ private:
 
 private:
     KeysType _keys_type = DUP_KEYS;
+    SortType _sort_type = SortType::LEXICAL;
+    size_t _sort_col_num = 0;
     std::vector<TabletColumn> _cols;
     std::unordered_map<std::string, int32_t> _field_name_to_index;
     size_t _num_columns = 0;
diff --git a/be/src/util/CMakeLists.txt b/be/src/util/CMakeLists.txt
index 1fe088a..985b78f 100644
--- a/be/src/util/CMakeLists.txt
+++ b/be/src/util/CMakeLists.txt
@@ -106,6 +106,7 @@ set(UTIL_FILES
   s3_storage_backend.cpp
   s3_util.cpp
   topn_counter.cpp
+  tuple_row_zorder_compare.cpp
 )
 
 if (WITH_MYSQL)
diff --git a/be/src/util/bit_util.h b/be/src/util/bit_util.h
index a4bf2ef..549ce60 100644
--- a/be/src/util/bit_util.h
+++ b/be/src/util/bit_util.h
@@ -24,6 +24,12 @@
 #include "gutil/bits.h"
 #include "gutil/port.h"
 #include "util/cpu_info.h"
+#ifdef __aarch64__
+#include "sse2neon.h"
+#else
+#include <emmintrin.h>
+#include <immintrin.h>
+#endif
 
 namespace doris {
 
@@ -328,6 +334,116 @@ public:
         if (PREDICT_FALSE(num_bits >= 64)) return 0;
         return v >> num_bits;
     }
+
+    static void ByteSwapScalar(void *dest, const void *source, int len) {
+        uint8_t *dst = reinterpret_cast<uint8_t *>(dest);
+        const uint8_t *src = reinterpret_cast<const uint8_t *>(source);
+        switch (len) {
+            case 1:
+                *reinterpret_cast<uint8_t *>(dst) = *reinterpret_cast<const 
uint8_t *>(src);
+                return;
+            case 2:
+                *reinterpret_cast<uint16_t *>(dst) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint16_t 
*>(src));
+                return;
+            case 3:
+                *reinterpret_cast<uint16_t *>(dst + 1) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint16_t 
*>(src));
+                *reinterpret_cast<uint8_t *>(dst) = *reinterpret_cast<const 
uint8_t *>(src + 2);
+                return;
+            case 4:
+                *reinterpret_cast<uint32_t *>(dst) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint32_t 
*>(src));
+                return;
+            case 5:
+                *reinterpret_cast<uint32_t *>(dst + 1) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint32_t 
*>(src));
+                *reinterpret_cast<uint8_t *>(dst) = *reinterpret_cast<const 
uint8_t *>(src + 4);
+                return;
+            case 6:
+                *reinterpret_cast<uint32_t *>(dst + 2) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint32_t 
*>(src));
+                *reinterpret_cast<uint16_t *>(dst) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint16_t 
*>(src + 4));
+                return;
+            case 7:
+                *reinterpret_cast<uint32_t *>(dst + 3) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint32_t 
*>(src));
+                *reinterpret_cast<uint16_t *>(dst + 1) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint16_t 
*>(src + 4));
+                *reinterpret_cast<uint8_t *>(dst) = *reinterpret_cast<const 
uint8_t *>(src + 6);
+                return;
+            case 8:
+                *reinterpret_cast<uint64_t *>(dst) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint64_t 
*>(src));
+                return;
+            case 9:
+                *reinterpret_cast<uint64_t *>(dst + 1) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint64_t 
*>(src));
+                *reinterpret_cast<uint8_t *>(dst) = *reinterpret_cast<const 
uint8_t *>(src + 8);
+                return;
+            case 10:
+                *reinterpret_cast<uint64_t *>(dst + 2) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint64_t 
*>(src));
+                *reinterpret_cast<uint16_t *>(dst) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint16_t 
*>(src + 8));
+                return;
+            case 11:
+                *reinterpret_cast<uint64_t *>(dst + 3) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint64_t 
*>(src));
+                *reinterpret_cast<uint16_t *>(dst + 1) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint16_t 
*>(src + 8));
+                *reinterpret_cast<uint8_t *>(dst) = *reinterpret_cast<const 
uint8_t *>(src + 10);
+                return;
+            case 12:
+                *reinterpret_cast<uint64_t *>(dst + 4) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint64_t 
*>(src));
+                *reinterpret_cast<uint32_t *>(dst) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint32_t 
*>(src + 8));
+                return;
+            case 13:
+                *reinterpret_cast<uint64_t *>(dst + 5) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint64_t 
*>(src));
+                *reinterpret_cast<uint32_t *>(dst + 1) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint32_t 
*>(src + 8));
+                *reinterpret_cast<uint8_t *>(dst) = *reinterpret_cast<const 
uint8_t *>(src + 12);
+                return;
+            case 14:
+                *reinterpret_cast<uint64_t *>(dst + 6) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint64_t 
*>(src));
+                *reinterpret_cast<uint32_t *>(dst + 2) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint32_t 
*>(src + 8));
+                *reinterpret_cast<uint16_t *>(dst) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint16_t 
*>(src + 12));
+                return;
+            case 15:
+                *reinterpret_cast<uint64_t *>(dst + 7) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint64_t 
*>(src));
+                *reinterpret_cast<uint32_t *>(dst + 3) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint32_t 
*>(src + 8));
+                *reinterpret_cast<uint16_t *>(dst + 1) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint16_t 
*>(src + 12));
+                *reinterpret_cast<uint8_t *>(dst) = *reinterpret_cast<const 
uint8_t *>(src + 14);
+                return;
+            case 16:
+                *reinterpret_cast<uint64_t *>(dst + 8) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint64_t 
*>(src));
+                *reinterpret_cast<uint64_t *>(dst) =
+                        BitUtil::byte_swap(*reinterpret_cast<const uint64_t 
*>(src + 8));
+                return;
+            default:
+                // Revert to slow loop-based swap.
+                ByteSwapScalarLoop(source, len, dest);
+                return;
+        }
+    }
+
+    static void ByteSwapScalarLoop(const void *src, int len, void *dst) {
+        //TODO: improve the performance of following code further using BSWAP 
intrinsic
+        uint8_t *d = reinterpret_cast<uint8_t *>(dst);
+        const uint8_t *s = reinterpret_cast<const uint8_t *>(src);
+        for (int i = 0; i < len; ++i) d[i] = s[len - i - 1];
+    }
 };
 
 } // namespace doris
diff --git a/be/src/util/tuple_row_zorder_compare.cpp 
b/be/src/util/tuple_row_zorder_compare.cpp
new file mode 100644
index 0000000..58318e6
--- /dev/null
+++ b/be/src/util/tuple_row_zorder_compare.cpp
@@ -0,0 +1,233 @@
+// 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.
+
+#include "util/tuple_row_zorder_compare.h"
+
+namespace doris {
+
+    RowComparator::RowComparator(Schema* schema) {
+
+    }
+
+    int RowComparator::operator()(const char* left, const char* right) const {
+        return -1;
+    }
+
+    TupleRowZOrderComparator::TupleRowZOrderComparator() {
+        _schema = nullptr;
+        _sort_col_num = 0;
+    }
+
+    TupleRowZOrderComparator::TupleRowZOrderComparator(int sort_col_num) {
+        _schema = nullptr;
+        _sort_col_num = sort_col_num;
+    }
+
+    TupleRowZOrderComparator::TupleRowZOrderComparator(Schema* schema, int 
sort_col_num)
+                            :_schema(schema), _sort_col_num(sort_col_num) {
+        _max_col_size = get_type_byte_size(_schema->column(0)->type());
+        for (size_t i = 1; i < _sort_col_num; ++i) {
+            if (_max_col_size <  
get_type_byte_size(_schema->column(i)->type())) {
+                _max_col_size =  
get_type_byte_size(_schema->column(i)->type());
+            }
+        }
+    }
+
+    int TupleRowZOrderComparator::compare(const char* lhs, const char* rhs) 
const {
+        ContiguousRow lhs_row(_schema, lhs);
+        ContiguousRow rhs_row(_schema, rhs);
+        if (_max_col_size <= 4) {
+            return compare_based_on_size<uint32_t, ContiguousRow>(lhs_row, 
rhs_row);
+        } else if (_max_col_size <= 8) {
+            return compare_based_on_size<uint64_t, ContiguousRow>(lhs_row, 
rhs_row);
+        } else {
+            return compare_based_on_size<uint128_t, ContiguousRow>(lhs_row, 
rhs_row);
+        }
+    }
+
+    void TupleRowZOrderComparator::max_col_size(const RowCursor& rc) {
+        _max_col_size = get_type_byte_size(rc.schema()->column(0)->type());
+        for (size_t i = 1; i < _sort_col_num; ++i) {
+            if (_max_col_size <  
get_type_byte_size(rc.schema()->column(i)->type())) {
+                _max_col_size =  
get_type_byte_size(rc.schema()->column(i)->type());
+            }
+        }
+    }
+
+    int TupleRowZOrderComparator::compare_row(const RowCursor& lhs, const 
RowCursor& rhs) {
+        max_col_size(lhs);
+        if (_max_col_size <= 4) {
+            return compare_based_on_size<uint32_t, const RowCursor>(lhs, rhs);
+        } else if (_max_col_size <= 8) {
+            return compare_based_on_size<uint64_t, const RowCursor>(lhs, lhs);
+        } else {
+            return compare_based_on_size<uint128_t, const RowCursor>(lhs, lhs);
+        }
+    }
+
+    template<typename U, typename LhsRowType>
+    int TupleRowZOrderComparator::compare_based_on_size(LhsRowType& lhs, 
LhsRowType& rhs) const {
+        auto less_msb = [](U x, U y) { return x < y && x < (x ^ y); };
+        FieldType type = lhs.schema()->column(0)->type();
+        U msd_lhs = get_shared_representation<U>(lhs.cell(0).is_null() ? 
nullptr : lhs.cell(0).cell_ptr(),
+                                                 type);
+        U msd_rhs = get_shared_representation<U>(rhs.cell(0).is_null() ? 
nullptr : rhs.cell(0).cell_ptr(),
+                                                 type);
+        for (int i = 1; i < _sort_col_num; ++i) {
+            type = lhs.schema()->column(i)->type();
+            const void *lhs_v = lhs.cell(i).is_null() ? nullptr : 
lhs.cell(i).cell_ptr();
+            const void *rhs_v = rhs.cell(i).is_null() ? nullptr : 
rhs.cell(i).cell_ptr();
+            U lhsi = get_shared_representation<U>(lhs_v, type);
+            U rhsi = get_shared_representation<U>(rhs_v, type);
+            if (less_msb(msd_lhs ^ msd_rhs, lhsi ^ rhsi)) {
+                msd_lhs = lhsi;
+                msd_rhs = rhsi;
+            }
+        }
+        return msd_lhs < msd_rhs ? -1 : (msd_lhs > msd_rhs ? 1 : 0);
+    }
+
+    template<typename U>
+    U TupleRowZOrderComparator::get_shared_representation(const void *val, 
FieldType type) const {
+        // The mask used for setting the sign bit correctly.
+        if (val == NULL) return 0;
+        constexpr U mask = (U) 1 << (sizeof(U) * 8 - 1);
+        switch (type) {
+            case FieldType::OLAP_FIELD_TYPE_NONE:
+                return 0;
+            case FieldType::OLAP_FIELD_TYPE_BOOL:
+                return static_cast<U>(*reinterpret_cast<const bool *>(val)) << 
(sizeof(U) * 8 - 1);
+            case FieldType::OLAP_FIELD_TYPE_UNSIGNED_TINYINT:
+            case FieldType::OLAP_FIELD_TYPE_TINYINT:
+                return get_shared_int_representation<U, int8_t>(
+                        *reinterpret_cast<const int8_t *>(val), mask);
+            case FieldType::OLAP_FIELD_TYPE_SMALLINT:
+            case FieldType::OLAP_FIELD_TYPE_UNSIGNED_SMALLINT:
+                return get_shared_int_representation<U, int16_t>(
+                        *reinterpret_cast<const int16_t *>(val), mask);
+            case FieldType::OLAP_FIELD_TYPE_INT:
+                return get_shared_int_representation<U, int32_t>(
+                        *reinterpret_cast<const int32_t *>(val), mask);
+            case FieldType::OLAP_FIELD_TYPE_DATETIME:
+            case FieldType::OLAP_FIELD_TYPE_DATE:
+            case FieldType::OLAP_FIELD_TYPE_BIGINT:
+            case FieldType::OLAP_FIELD_TYPE_UNSIGNED_BIGINT:
+                return get_shared_int_representation<U, int64_t>(
+                        *reinterpret_cast<const int64_t *>(val), mask);
+            case FieldType::OLAP_FIELD_TYPE_LARGEINT:
+                return static_cast<U>(*reinterpret_cast<const int128_t 
*>(val)) ^ mask;
+            case FieldType::OLAP_FIELD_TYPE_FLOAT:
+                return get_shared_float_representation<U, float>(val, mask);
+            case FieldType::OLAP_FIELD_TYPE_DISCRETE_DOUBLE:
+            case FieldType::OLAP_FIELD_TYPE_DOUBLE:
+                return get_shared_float_representation<U, double>(val, mask);
+            case FieldType::OLAP_FIELD_TYPE_CHAR:
+            case FieldType::OLAP_FIELD_TYPE_VARCHAR:{
+                const StringValue *string_value = reinterpret_cast<const 
StringValue *>(val);
+                return get_shared_string_representation<U>(string_value->ptr, 
string_value->len);
+            }
+            case FieldType::OLAP_FIELD_TYPE_DECIMAL: {
+                decimal12_t decimal_val = *reinterpret_cast<const 
decimal12_t*>(val);
+                int128_t value = 
decimal_val.integer*DecimalV2Value::ONE_BILLION + decimal_val.fraction;
+                return static_cast<U>(value) ^ mask;
+            }
+            default:
+                return 0;
+        }
+    }
+
+    template<typename U, typename T>
+    U inline TupleRowZOrderComparator::get_shared_int_representation(const T 
val, U mask) const {
+        uint64_t shift_size = static_cast<uint64_t>(
+                std::max(static_cast<int64_t>((sizeof(U) - sizeof(T)) * 8), 
(int64_t) 0));
+        return (static_cast<U>(val) << shift_size) ^ mask;
+    }
+
+    template<typename U, typename T>
+    U inline TupleRowZOrderComparator::get_shared_float_representation(const 
void *val, U mask) const {
+        int64_t tmp;
+        T floating_value = *reinterpret_cast<const T *>(val);
+        memcpy(&tmp, &floating_value, sizeof(T));
+        if (UNLIKELY(std::isnan(floating_value))) return 0;
+        if (floating_value < 0.0) {
+            // Flipping all bits for negative values.
+            return static_cast<U>(~tmp) << std::max((sizeof(U) - sizeof(T)) * 
8, (uint64_t) 0);
+        } else {
+            // Flipping only first bit.
+            return (static_cast<U>(tmp) << std::max((sizeof(U) - sizeof(T)) * 
8, (uint64_t) 0)) ^
+                   mask;
+        }
+    }
+
+    template<typename U>
+    U inline TupleRowZOrderComparator::get_shared_string_representation(const 
char *char_ptr,
+                                              int length) const {
+        int len = length < sizeof(U) ? length : sizeof(U);
+        if (len == 0) return 0;
+        U dst = 0;
+        // We copy the bytes from the string but swap the bytes because of 
integer endianness.
+        BitUtil::ByteSwapScalar(&dst, char_ptr, len);
+        return dst << ((sizeof(U) - len) * 8);
+    }
+
+    int TupleRowZOrderComparator::operator()(const char* lhs, const char* rhs) 
const {
+        int result = compare(lhs, rhs);
+        return result;
+    }
+
+    int TupleRowZOrderComparator::get_type_byte_size(FieldType type) const {
+        switch (type) {
+            case FieldType::OLAP_FIELD_TYPE_OBJECT:
+            case FieldType::OLAP_FIELD_TYPE_HLL:
+            case FieldType::OLAP_FIELD_TYPE_STRUCT:
+            case FieldType::OLAP_FIELD_TYPE_ARRAY:
+            case FieldType::OLAP_FIELD_TYPE_MAP:
+            case FieldType::OLAP_FIELD_TYPE_CHAR:
+            case FieldType::OLAP_FIELD_TYPE_VARCHAR:
+                return 0;
+            case FieldType::OLAP_FIELD_TYPE_NONE:
+            case FieldType::OLAP_FIELD_TYPE_BOOL:
+            case FieldType::OLAP_FIELD_TYPE_UNSIGNED_TINYINT:
+            case FieldType::OLAP_FIELD_TYPE_TINYINT:
+                return 1;
+            case FieldType::OLAP_FIELD_TYPE_SMALLINT:
+            case FieldType::OLAP_FIELD_TYPE_UNSIGNED_SMALLINT:
+                return 2;
+            case FieldType::OLAP_FIELD_TYPE_FLOAT:
+            case FieldType::OLAP_FIELD_TYPE_INT:
+            case FieldType::OLAP_FIELD_TYPE_UNSIGNED_INT:
+                return 4;
+            case FieldType::OLAP_FIELD_TYPE_DISCRETE_DOUBLE:
+            case FieldType::OLAP_FIELD_TYPE_DOUBLE:
+            case FieldType::OLAP_FIELD_TYPE_BIGINT:
+            case FieldType::OLAP_FIELD_TYPE_UNSIGNED_BIGINT:
+                return 8;
+            case FieldType::OLAP_FIELD_TYPE_DECIMAL:
+            case FieldType::OLAP_FIELD_TYPE_LARGEINT:
+            case FieldType::OLAP_FIELD_TYPE_DATETIME:
+            case FieldType::OLAP_FIELD_TYPE_DATE:
+                return 16;
+            case FieldType::OLAP_FIELD_TYPE_UNKNOWN:
+                DCHECK(false);
+                break;
+            default:
+                DCHECK(false);
+        }
+        return -1;
+    }
+
+}
diff --git a/be/src/util/tuple_row_zorder_compare.h 
b/be/src/util/tuple_row_zorder_compare.h
new file mode 100644
index 0000000..e44008c
--- /dev/null
+++ b/be/src/util/tuple_row_zorder_compare.h
@@ -0,0 +1,70 @@
+// 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.
+
+#ifndef DORIS_TUPLE_ROW_ZORDER_COMPARE_H
+#define DORIS_TUPLE_ROW_ZORDER_COMPARE_H
+
+#include "exec/sort_exec_exprs.h"
+#include "exprs/expr.h"
+#include "exprs/expr_context.h"
+#include "runtime/descriptors.h"
+#include "runtime/raw_value.h"
+#include "runtime/tuple.h"
+#include "runtime/tuple_row.h"
+#include "olap/schema.h"
+#include "olap/row.h"
+#include "olap/row_cursor.h"
+
+namespace doris {
+class RowComparator {
+public:
+    RowComparator()=default;
+    RowComparator(Schema* schema);
+    virtual int operator()(const char* left, const char* right) const;
+};
+
+class TupleRowZOrderComparator: public RowComparator {
+
+private:
+    typedef __uint128_t uint128_t;
+    int _max_col_size = 0;
+    const Schema* _schema;
+    int _sort_col_num = 0;
+
+public:
+    TupleRowZOrderComparator();
+    TupleRowZOrderComparator(int sort_col_num);
+    TupleRowZOrderComparator(Schema* schema, int sort_col_num);
+    int compare(const char* lhs, const char* rhs) const;
+    void max_col_size(const RowCursor& rc);
+    int compare_row(const RowCursor& lhs, const RowCursor& rhs);
+    template<typename U, typename LhsRowType>
+    int compare_based_on_size(LhsRowType& lhs, LhsRowType& rhs) const;
+    template<typename U>
+    U get_shared_representation(const void *val, FieldType type) const;
+    template<typename U, typename T>
+    U inline get_shared_int_representation(const T val, U mask) const;
+    template<typename U, typename T>
+    U inline get_shared_float_representation(const void *val, U mask) const;
+    template<typename U>
+    U inline get_shared_string_representation(const char *char_ptr, int 
length) const;
+    virtual int operator()(const char* lhs, const char* rhs) const;
+    int get_type_byte_size(FieldType type) const;
+};
+}
+
+#endif //DORIS_TUPLE_ROW_ZORDER_COMPARE_H
\ No newline at end of file
diff --git a/be/test/olap/skiplist_test.cpp b/be/test/olap/skiplist_test.cpp
index eb5831a..7c76ac5 100644
--- a/be/test/olap/skiplist_test.cpp
+++ b/be/test/olap/skiplist_test.cpp
@@ -55,7 +55,7 @@ TEST_F(SkipTest, Empty) {
     std::shared_ptr<MemTracker> tracker(new MemTracker(-1));
     std::unique_ptr<MemPool> mem_pool(new MemPool(tracker.get()));
 
-    TestComparator cmp;
+    TestComparator* cmp = new TestComparator();
     SkipList<Key, TestComparator> list(cmp, mem_pool.get(), false);
     ASSERT_TRUE(!list.Contains(10));
 
@@ -67,6 +67,7 @@ TEST_F(SkipTest, Empty) {
     ASSERT_TRUE(!iter.Valid());
     iter.SeekToLast();
     ASSERT_TRUE(!iter.Valid());
+    delete cmp;
 }
 
 TEST_F(SkipTest, InsertAndLookup) {
@@ -77,7 +78,7 @@ TEST_F(SkipTest, InsertAndLookup) {
     const int R = 5000;
     Random rnd(1000);
     std::set<Key> keys;
-    TestComparator cmp;
+    TestComparator* cmp = new TestComparator();
     SkipList<Key, TestComparator> list(cmp, mem_pool.get(), false);
     for (int i = 0; i < N; i++) {
         Key key = rnd.Next() % R;
@@ -147,6 +148,7 @@ TEST_F(SkipTest, InsertAndLookup) {
         }
         ASSERT_TRUE(!iter.Valid());
     }
+    delete cmp;
 }
 
 // Only non-DUP model will use Find() and InsertWithHint().
@@ -158,7 +160,7 @@ TEST_F(SkipTest, InsertWithHintNoneDupModel) {
     const int R = 5000;
     Random rnd(1000);
     std::set<Key> keys;
-    TestComparator cmp;
+    TestComparator* cmp = new TestComparator();
     SkipList<Key, TestComparator> list(cmp, mem_pool.get(), false);
     SkipList<Key, TestComparator>::Hint hint;
     for (int i = 0; i < N; i++) {
@@ -179,6 +181,7 @@ TEST_F(SkipTest, InsertWithHintNoneDupModel) {
             ASSERT_EQ(keys.count(i), 0);
         }
     }
+    delete cmp;
 }
 
 // We want to make sure that with a single writer and multiple
@@ -259,7 +262,7 @@ private:
 
     std::shared_ptr<MemTracker> _mem_tracker;
     std::unique_ptr<MemPool> _mem_pool;
-
+    std::shared_ptr<TestComparator> _comparator;
     // SkipList is not protected by _mu.  We just use a single writer
     // thread to modify it.
     SkipList<Key, TestComparator> _list;
@@ -268,8 +271,9 @@ public:
     ConcurrentTest()
             : _mem_tracker(new MemTracker(-1)),
               _mem_pool(new MemPool(_mem_tracker.get())),
-              _list(TestComparator(), _mem_pool.get(), false) {}
-
+              _comparator(new TestComparator()),
+              _list(_comparator.get(), _mem_pool.get(), false) {}
+    
     // REQUIRES: External synchronization
     void write_step(Random* rnd) {
         const uint32_t k = rnd->Next() % K;
diff --git a/be/test/olap/test_data/header_without_inc_rs.txt 
b/be/test/olap/test_data/header_without_inc_rs.txt
index edd7d03..11139bb 100644
--- a/be/test/olap/test_data/header_without_inc_rs.txt
+++ b/be/test/olap/test_data/header_without_inc_rs.txt
@@ -51,7 +51,9 @@
         "next_column_unique_id": 3,
         "is_in_memory": false,
         "delete_sign_idx": -1,
-        "sequence_col_idx": -1
+        "sequence_col_idx": -1,
+        "sort_type": "LEXICAL",
+        "sort_col_num": 0
     },
     "rs_metas": [
         {
diff --git a/be/test/util/CMakeLists.txt b/be/test/util/CMakeLists.txt
index 73decdf..ec88f8c 100644
--- a/be/test/util/CMakeLists.txt
+++ b/be/test/util/CMakeLists.txt
@@ -74,5 +74,6 @@ ADD_BE_TEST(broker_storage_backend_test)
 ADD_BE_TEST(sort_heap_test)
 ADD_BE_TEST(counts_test)
 ADD_BE_TEST(date_func_test)
+ADD_BE_TEST(tuple_row_zorder_compare_test)
 
 target_link_libraries(Test_util Common Util Gutil ${Boost_LIBRARIES} glog 
gflags fmt protobuf)
diff --git a/be/test/util/tuple_row_zorder_compare_test.cpp 
b/be/test/util/tuple_row_zorder_compare_test.cpp
new file mode 100644
index 0000000..b83b53c
--- /dev/null
+++ b/be/test/util/tuple_row_zorder_compare_test.cpp
@@ -0,0 +1,1120 @@
+// 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.
+
+#include "util/json_util.h"
+#include <gtest/gtest.h>
+#include "common/logging.h"
+#include "exec/sort_exec_exprs.h"
+#include "exprs/expr.h"
+#include "exprs/expr_context.h"
+#include "runtime/descriptors.h"
+#include "runtime/raw_value.h"
+#include "runtime/tuple.h"
+#include "runtime/tuple_row.h"
+#include "olap/schema.h"
+#include "olap/row.h"
+#include "util/tuple_row_zorder_compare.h"
+#include "runtime/large_int_value.h"
+#include "olap/memtable.h"
+
+namespace doris {
+    class TupleRowZOrderCompareTest : public testing::Test {
+    public:
+        TupleRowZOrderComparator _comparator;
+        ObjectPool _agg_buffer_pool;
+        std::unique_ptr<MemPool> _buffer_mem_pool;
+        MemTracker* _mem_tracker;
+        Schema* _schema;
+        std::vector<SlotDescriptor*>* _slot_descs;
+
+    public:
+        TupleRowZOrderCompareTest() {
+            _mem_tracker = new MemTracker(-1);
+            _buffer_mem_pool.reset(new MemPool(_mem_tracker));
+        }
+
+        template <typename T, bool IS_FIRST_SLOT_NULL = false>
+        int CompareInt8Test(T lval1, T lval2, T rval1, T rval2) {
+            int col_num = 2;
+            std::vector<SlotDescriptor*> slot_descs;
+            for (int i = 1; i <= col_num; i++) {
+                TSlotDescriptor slot_desc;
+                slot_desc.id = i;
+                slot_desc.parent = 0;
+                TTypeDesc type;
+                {
+                    TTypeNode node;
+                    node.__set_type(TTypeNodeType::SCALAR);
+                    TScalarType scalar_type;
+                    scalar_type.__set_type(TPrimitiveType::type::TINYINT);
+                    node.__set_scalar_type(scalar_type);
+                    type.types.push_back(node);
+                }
+                slot_desc.slotType = type;
+                slot_desc.columnPos = i;
+                slot_desc.byteOffset = (i-1)*sizeof(int8_t) + 1*i;
+
+                slot_desc.nullIndicatorByte = 0+(i-1)*sizeof(int8_t) + 1*(i-1);
+                slot_desc.nullIndicatorBit = 1;
+                std::ostringstream ss;
+                ss << "col_" << i;
+                slot_desc.colName = ss.str();
+                slot_desc.slotIdx = i;
+                slot_desc.isMaterialized = true;
+                SlotDescriptor* slot = new SlotDescriptor(slot_desc);
+                slot_descs.push_back(slot);
+            }
+            _slot_descs = &slot_descs;
+
+            std::vector<TabletColumn> col_schemas;
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_TINYINT, true);
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_TINYINT, true);
+            Schema schema(col_schemas, col_num);
+            _schema = &schema;
+
+            TupleRowZOrderComparator comparator(&schema, 2);
+            _comparator = comparator;
+
+            Tuple* lhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(int8_t), _buffer_mem_pool.get());
+            if (IS_FIRST_SLOT_NULL) {
+                lhs_tuple->set_null(NullIndicatorOffset(0, 1));
+            }
+            FillMem(lhs_tuple, 1, lval1, lval2);
+            uint8_t* lhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(int8_t));
+            ContiguousRow lhs_row(_schema, lhs_tuple_buf);
+            tuple_to_row(lhs_tuple, &lhs_row, _buffer_mem_pool.get());
+
+            Tuple* rhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(int8_t), _buffer_mem_pool.get());
+            FillMem(rhs_tuple, 1, rval1, rval2);
+            uint8_t* rhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(int8_t));
+            ContiguousRow rhs_row(_schema, rhs_tuple_buf);
+            tuple_to_row(rhs_tuple, &rhs_row, _buffer_mem_pool.get());
+            int result = _comparator.compare(reinterpret_cast<const 
char*>(lhs_row.row_ptr()),
+                                             reinterpret_cast<const 
char*>(rhs_row.row_ptr()));
+            return result;
+        }
+
+        template <typename T, bool IS_FIRST_SLOT_NULL = false>
+        int CompareInt16Test(T lval1, T lval2, T rval1, T rval2) {
+            int col_num = 2;
+            std::vector<SlotDescriptor*> slot_descs;
+            for (int i = 1; i <= col_num; i++) {
+                TSlotDescriptor slot_desc;
+                slot_desc.id = i;
+                slot_desc.parent = 0;
+                TTypeDesc type;
+                {
+                    TTypeNode node;
+                    node.__set_type(TTypeNodeType::SCALAR);
+                    TScalarType scalar_type;
+                    scalar_type.__set_type(TPrimitiveType::type::SMALLINT);
+                    node.__set_scalar_type(scalar_type);
+                    type.types.push_back(node);
+                }
+                slot_desc.slotType = type;
+                slot_desc.columnPos = i;
+                slot_desc.byteOffset = (i-1)*sizeof(int16_t) + 1*i;
+
+                slot_desc.nullIndicatorByte = 0+(i-1)*sizeof(int16_t) + 
1*(i-1);
+                slot_desc.nullIndicatorBit = 1;
+                std::ostringstream ss;
+                ss << "col_" << i;
+                slot_desc.colName = ss.str();
+                slot_desc.slotIdx = i;
+                slot_desc.isMaterialized = true;
+                SlotDescriptor* slot = new SlotDescriptor(slot_desc);
+                slot_descs.push_back(slot);
+            }
+            _slot_descs = &slot_descs;
+
+            std::vector<TabletColumn> col_schemas;
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_SMALLINT, true);
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_SMALLINT, true);
+            Schema schema(col_schemas, col_num);
+            _schema = &schema;
+
+            TupleRowZOrderComparator comparator(&schema, 2);
+            _comparator = comparator;
+
+            Tuple* lhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(int16_t), _buffer_mem_pool.get());
+            if (IS_FIRST_SLOT_NULL) {
+                lhs_tuple->set_null(NullIndicatorOffset(0, 1));
+            }
+            FillMem(lhs_tuple, 1, lval1, lval2);
+            uint8_t* lhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(int16_t));
+            ContiguousRow lhs_row(_schema, lhs_tuple_buf);
+            tuple_to_row(lhs_tuple, &lhs_row, _buffer_mem_pool.get());
+
+            Tuple* rhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(int16_t), _buffer_mem_pool.get());
+            FillMem(rhs_tuple, 1, rval1, rval2);
+            uint8_t* rhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(int16_t));
+            ContiguousRow rhs_row(_schema, rhs_tuple_buf);
+            tuple_to_row(rhs_tuple, &rhs_row, _buffer_mem_pool.get());
+            int result = _comparator.compare(reinterpret_cast<const 
char*>(lhs_row.row_ptr()),
+                                             reinterpret_cast<const 
char*>(rhs_row.row_ptr()));
+            return result;
+        }
+
+        template <typename T, bool IS_FIRST_SLOT_NULL = false>
+        int CompareIntTest(T lval1, T lval2, T rval1, T rval2) {
+            int col_num = 2;
+            std::vector<SlotDescriptor*> slot_descs;
+            for (int i = 1; i <= col_num; i++) {
+                TSlotDescriptor slot_desc;
+                slot_desc.id = i;
+                slot_desc.parent = 0;
+                TTypeDesc type;
+                {
+                    TTypeNode node;
+                    node.__set_type(TTypeNodeType::SCALAR);
+                    TScalarType scalar_type;
+                    scalar_type.__set_type(TPrimitiveType::type::INT);
+                    node.__set_scalar_type(scalar_type);
+                    type.types.push_back(node);
+                }
+                slot_desc.slotType = type;
+                slot_desc.columnPos = i;
+                slot_desc.byteOffset = (i-1)*sizeof(int32_t) + 1*i;
+
+                slot_desc.nullIndicatorByte = 0+(i-1)*sizeof(int32_t) + 
1*(i-1);
+                slot_desc.nullIndicatorBit = 1;
+                std::ostringstream ss;
+                ss << "col_" << i;
+                slot_desc.colName = ss.str();
+                slot_desc.slotIdx = i;
+                slot_desc.isMaterialized = true;
+                SlotDescriptor* slot = new SlotDescriptor(slot_desc);
+                slot_descs.push_back(slot);
+            }
+            _slot_descs = &slot_descs;
+
+            std::vector<TabletColumn> col_schemas;
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_INT, true);
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_INT, true);
+            Schema schema(col_schemas, col_num);
+            _schema = &schema;
+
+            TupleRowZOrderComparator comparator(&schema, 2);
+            _comparator = comparator;
+
+            Tuple* lhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(int32_t), _buffer_mem_pool.get());
+            if (IS_FIRST_SLOT_NULL) {
+                lhs_tuple->set_null(NullIndicatorOffset(0, 1));
+            }
+            FillMem(lhs_tuple, 1, lval1, lval2);
+            uint8_t* lhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(int32_t));
+            ContiguousRow lhs_row(_schema, lhs_tuple_buf);
+            tuple_to_row(lhs_tuple, &lhs_row, _buffer_mem_pool.get());
+
+            Tuple* rhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(int32_t), _buffer_mem_pool.get());
+            FillMem(rhs_tuple, 1, rval1, rval2);
+            uint8_t* rhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(int32_t));
+            ContiguousRow rhs_row(_schema, rhs_tuple_buf);
+            tuple_to_row(rhs_tuple, &rhs_row, _buffer_mem_pool.get());
+            int result = _comparator.compare(reinterpret_cast<const 
char*>(lhs_row.row_ptr()),
+                                             reinterpret_cast<const 
char*>(rhs_row.row_ptr()));
+            return result;
+        }
+
+        template <typename T, bool IS_FIRST_SLOT_NULL = false>
+        int CompareInt64Test(T lval1, T lval2, T rval1, T rval2) {
+            int col_num = 2;
+            std::vector<SlotDescriptor*> slot_descs;
+            for (int i = 1; i <= col_num; i++) {
+                TSlotDescriptor slot_desc;
+                slot_desc.id = i;
+                slot_desc.parent = 0;
+                TTypeDesc type;
+                {
+                    TTypeNode node;
+                    node.__set_type(TTypeNodeType::SCALAR);
+                    TScalarType scalar_type;
+                    scalar_type.__set_type(TPrimitiveType::type::BIGINT);
+                    node.__set_scalar_type(scalar_type);
+                    type.types.push_back(node);
+                }
+                slot_desc.slotType = type;
+                slot_desc.columnPos = i;
+                slot_desc.byteOffset = (i-1)*sizeof(int64_t) + 1*i;
+
+                slot_desc.nullIndicatorByte = 0+(i-1)*sizeof(int64_t) + 
1*(i-1);
+                slot_desc.nullIndicatorBit = 1;
+                std::ostringstream ss;
+                ss << "col_" << i;
+                slot_desc.colName = ss.str();
+                slot_desc.slotIdx = i;
+                slot_desc.isMaterialized = true;
+                SlotDescriptor* slot = new SlotDescriptor(slot_desc);
+                slot_descs.push_back(slot);
+            }
+            _slot_descs = &slot_descs;
+
+            std::vector<TabletColumn> col_schemas;
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_BIGINT, true);
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_BIGINT, true);
+            Schema schema(col_schemas, col_num);
+            _schema = &schema;
+
+            TupleRowZOrderComparator comparator(&schema, 2);
+            _comparator = comparator;
+
+            Tuple* lhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(int64_t), _buffer_mem_pool.get());
+            if (IS_FIRST_SLOT_NULL) {
+                lhs_tuple->set_null(NullIndicatorOffset(0, 1));
+            }
+            FillMem(lhs_tuple, 1, lval1, lval2);
+            uint8_t* lhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(int64_t));
+            ContiguousRow lhs_row(_schema, lhs_tuple_buf);
+            tuple_to_row(lhs_tuple, &lhs_row, _buffer_mem_pool.get());
+
+            Tuple* rhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(int64_t), _buffer_mem_pool.get());
+            FillMem(rhs_tuple, 1, rval1, rval2);
+            uint8_t* rhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(int64_t));
+            ContiguousRow rhs_row(_schema, rhs_tuple_buf);
+            tuple_to_row(rhs_tuple, &rhs_row, _buffer_mem_pool.get());
+            int result = _comparator.compare(reinterpret_cast<const 
char*>(lhs_row.row_ptr()),
+                                             reinterpret_cast<const 
char*>(rhs_row.row_ptr()));
+            return result;
+        }
+
+        template <typename T, bool IS_FIRST_SLOT_NULL = false>
+        int CompareInt128Test(T lval1, T lval2, T rval1, T rval2) {
+            int col_num = 2;
+            std::vector<SlotDescriptor*> slot_descs;
+            for (int i = 1; i <= col_num; i++) {
+                TSlotDescriptor slot_desc;
+                slot_desc.id = i;
+                slot_desc.parent = 0;
+                TTypeDesc type;
+                {
+                    TTypeNode node;
+                    node.__set_type(TTypeNodeType::SCALAR);
+                    TScalarType scalar_type;
+                    scalar_type.__set_type(TPrimitiveType::type::LARGEINT);
+                    node.__set_scalar_type(scalar_type);
+                    type.types.push_back(node);
+                }
+                slot_desc.slotType = type;
+                slot_desc.columnPos = i;
+                slot_desc.byteOffset = (i-1)*sizeof(int128_t) + 1*i;
+
+                slot_desc.nullIndicatorByte = 0+(i-1)*sizeof(int128_t) + 
1*(i-1);
+                slot_desc.nullIndicatorBit = 1;
+                std::ostringstream ss;
+                ss << "col_" << i;
+                slot_desc.colName = ss.str();
+                slot_desc.slotIdx = i;
+                slot_desc.isMaterialized = true;
+                SlotDescriptor* slot = new SlotDescriptor(slot_desc);
+                slot_descs.push_back(slot);
+            }
+            _slot_descs = &slot_descs;
+
+            std::vector<TabletColumn> col_schemas;
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_LARGEINT, true);
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_LARGEINT, true);
+            Schema schema(col_schemas, col_num);
+            _schema = &schema;
+
+            TupleRowZOrderComparator comparator(&schema, 2);
+            _comparator = comparator;
+            MemTable::RowCursorComparator row_comparator(&schema);
+
+            Tuple* lhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(int128_t), _buffer_mem_pool.get());
+            if (IS_FIRST_SLOT_NULL) {
+                lhs_tuple->set_null(NullIndicatorOffset(0, 1));
+            }
+            FillMem(lhs_tuple, 1, lval1, lval2);
+            uint8_t* lhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(int128_t));
+            ContiguousRow lhs_row(_schema, lhs_tuple_buf);
+            tuple_to_row(lhs_tuple, &lhs_row, _buffer_mem_pool.get());
+
+            Tuple* rhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(int128_t), _buffer_mem_pool.get());
+            FillMem(rhs_tuple, 1, rval1, rval2);
+            uint8_t* rhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(int128_t));
+            ContiguousRow rhs_row(_schema, rhs_tuple_buf);
+            tuple_to_row(rhs_tuple, &rhs_row, _buffer_mem_pool.get());
+            int result = _comparator.compare(reinterpret_cast<const 
char*>(lhs_row.row_ptr()),
+                                             reinterpret_cast<const 
char*>(rhs_row.row_ptr()));
+            return result;
+        }
+
+        template <typename T, bool IS_FIRST_SLOT_NULL = false>
+        int CompareFloatTest(T lval1, T lval2, T rval1, T rval2) {
+            int col_num = 2;
+            std::vector<SlotDescriptor*> slot_descs;
+            for (int i = 1; i <= col_num; i++) {
+                TSlotDescriptor slot_desc;
+                slot_desc.id = i;
+                slot_desc.parent = 0;
+                TTypeDesc type;
+                {
+                    TTypeNode node;
+                    node.__set_type(TTypeNodeType::SCALAR);
+                    TScalarType scalar_type;
+                    scalar_type.__set_type(TPrimitiveType::type::FLOAT);
+                    node.__set_scalar_type(scalar_type);
+                    type.types.push_back(node);
+                }
+                slot_desc.slotType = type;
+                slot_desc.columnPos = i;
+                slot_desc.byteOffset = (i-1)*sizeof(float) + 1*i;
+
+                slot_desc.nullIndicatorByte = 0+(i-1)*sizeof(float) + 1*(i-1);
+                slot_desc.nullIndicatorBit = 1;
+                std::ostringstream ss;
+                ss << "col_" << i;
+                slot_desc.colName = ss.str();
+                slot_desc.slotIdx = i;
+                slot_desc.isMaterialized = true;
+                SlotDescriptor* slot = new SlotDescriptor(slot_desc);
+                slot_descs.push_back(slot);
+            }
+            _slot_descs = &slot_descs;
+
+            std::vector<TabletColumn> col_schemas;
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_FLOAT, true);
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_FLOAT, true);
+            Schema schema(col_schemas, col_num);
+            _schema = &schema;
+
+            TupleRowZOrderComparator comparator(&schema, 2);
+            _comparator = comparator;
+
+            Tuple* lhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(float), _buffer_mem_pool.get());
+            if (IS_FIRST_SLOT_NULL) {
+                lhs_tuple->set_null(NullIndicatorOffset(0, 1));
+            }
+            FillMem(lhs_tuple, 1, lval1, lval2);
+            uint8_t* lhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(float));
+            ContiguousRow lhs_row(_schema, lhs_tuple_buf);
+            tuple_to_row(lhs_tuple, &lhs_row, _buffer_mem_pool.get());
+
+            Tuple* rhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(float), _buffer_mem_pool.get());
+            FillMem(rhs_tuple, 1, rval1, rval2);
+            uint8_t* rhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(float));
+            ContiguousRow rhs_row(_schema, rhs_tuple_buf);
+            tuple_to_row(rhs_tuple, &rhs_row, _buffer_mem_pool.get());
+            int result = _comparator.compare(reinterpret_cast<const 
char*>(lhs_row.row_ptr()),
+                                             reinterpret_cast<const 
char*>(rhs_row.row_ptr()));
+            return result;
+        }
+
+        template <typename T, bool IS_FIRST_SLOT_NULL = false>
+        int CompareDoubleTest(T lval1, T lval2, T rval1, T rval2) {
+            int col_num = 2;
+            std::vector<SlotDescriptor*> slot_descs;
+            for (int i = 1; i <= col_num; i++) {
+                TSlotDescriptor slot_desc;
+                slot_desc.id = i;
+                slot_desc.parent = 0;
+                TTypeDesc type;
+                {
+                    TTypeNode node;
+                    node.__set_type(TTypeNodeType::SCALAR);
+                    TScalarType scalar_type;
+                    scalar_type.__set_type(TPrimitiveType::type::DOUBLE);
+                    node.__set_scalar_type(scalar_type);
+                    type.types.push_back(node);
+                }
+                slot_desc.slotType = type;
+                slot_desc.columnPos = i;
+                slot_desc.byteOffset = (i-1)*sizeof(double) + 1*i;
+
+                slot_desc.nullIndicatorByte = 0+(i-1)*sizeof(double) + 1*(i-1);
+                slot_desc.nullIndicatorBit = 1;
+                std::ostringstream ss;
+                ss << "col_" << i;
+                slot_desc.colName = ss.str();
+                slot_desc.slotIdx = i;
+                slot_desc.isMaterialized = true;
+                SlotDescriptor* slot = new SlotDescriptor(slot_desc);
+                slot_descs.push_back(slot);
+            }
+            _slot_descs = &slot_descs;
+
+            std::vector<TabletColumn> col_schemas;
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_DOUBLE, true);
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_DOUBLE, true);
+            Schema schema(col_schemas, col_num);
+            _schema = &schema;
+
+            TupleRowZOrderComparator comparator(&schema, 2);
+            _comparator = comparator;
+
+            Tuple* lhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(double), _buffer_mem_pool.get());
+            if (IS_FIRST_SLOT_NULL) {
+                lhs_tuple->set_null(NullIndicatorOffset(0, 1));
+            }
+            FillMem(lhs_tuple, 1, lval1, lval2);
+            uint8_t* lhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(double));
+            ContiguousRow lhs_row(_schema, lhs_tuple_buf);
+            tuple_to_row(lhs_tuple, &lhs_row, _buffer_mem_pool.get());
+
+            Tuple* rhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(double), _buffer_mem_pool.get());
+            FillMem(rhs_tuple, 1, rval1, rval2);
+            uint8_t* rhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(double));
+            ContiguousRow rhs_row(_schema, rhs_tuple_buf);
+            tuple_to_row(rhs_tuple, &rhs_row, _buffer_mem_pool.get());
+            int result = _comparator.compare(reinterpret_cast<const 
char*>(lhs_row.row_ptr()),
+                                             reinterpret_cast<const 
char*>(rhs_row.row_ptr()));
+            return result;
+        }
+
+        template <typename T, bool IS_FIRST_SLOT_NULL = false>
+        int CompareBoolTest(T lval1, T lval2, T rval1, T rval2) {
+            int col_num = 2;
+            std::vector<SlotDescriptor*> slot_descs;
+            for (int i = 1; i <= col_num; i++) {
+                TSlotDescriptor slot_desc;
+                slot_desc.id = i;
+                slot_desc.parent = 0;
+                TTypeDesc type;
+                {
+                    TTypeNode node;
+                    node.__set_type(TTypeNodeType::SCALAR);
+                    TScalarType scalar_type;
+                    scalar_type.__set_type(TPrimitiveType::type::BOOLEAN);
+                    node.__set_scalar_type(scalar_type);
+                    type.types.push_back(node);
+                }
+                slot_desc.slotType = type;
+                slot_desc.columnPos = i;
+                slot_desc.byteOffset = (i-1)*sizeof(bool) + 1*i;
+                slot_desc.nullIndicatorByte = 0+(i-1)*sizeof(bool) + 1*(i-1);
+                slot_desc.nullIndicatorBit = 1;
+                std::ostringstream ss;
+                ss << "col_" << i;
+                slot_desc.colName = ss.str();
+                slot_desc.slotIdx = i;
+                slot_desc.isMaterialized = true;
+                SlotDescriptor* slot = new SlotDescriptor(slot_desc);
+                slot_descs.push_back(slot);
+            }
+            _slot_descs = &slot_descs;
+
+            std::vector<TabletColumn> col_schemas;
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_BOOL, true);
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_BOOL, true);
+            Schema schema(col_schemas, col_num);
+            _schema = &schema;
+
+            TupleRowZOrderComparator comparator(&schema, 2);
+            _comparator = comparator;
+
+            Tuple* lhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(bool), _buffer_mem_pool.get());
+            if (IS_FIRST_SLOT_NULL) {
+                lhs_tuple->set_null(NullIndicatorOffset(0, 1));
+            }
+            FillMem(lhs_tuple, 1, lval1, lval2);
+            uint8_t* lhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(bool));
+            ContiguousRow lhs_row(_schema, lhs_tuple_buf);
+            tuple_to_row(lhs_tuple, &lhs_row, _buffer_mem_pool.get());
+
+            Tuple* rhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(bool), _buffer_mem_pool.get());
+            FillMem(rhs_tuple, 1, rval1, rval2);
+            uint8_t* rhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(bool));
+            ContiguousRow rhs_row(_schema, rhs_tuple_buf);
+            tuple_to_row(rhs_tuple, &rhs_row, _buffer_mem_pool.get());
+            int result = _comparator.compare(reinterpret_cast<const 
char*>(lhs_row.row_ptr()),
+                                             reinterpret_cast<const 
char*>(rhs_row.row_ptr()));
+            return result;
+        }
+
+        template <typename T, bool IS_FIRST_SLOT_NULL = false>
+        int CompareCharTest(T lval1, T lval2, T rval1, T rval2) {
+            int col_num = 2;
+            std::vector<SlotDescriptor*> slot_descs;
+            for (int i = 1; i <= col_num; i++) {
+                TSlotDescriptor slot_desc;
+                slot_desc.id = i;
+                slot_desc.parent = 0;
+                TTypeDesc type;
+                {
+                    TTypeNode node;
+                    node.__set_type(TTypeNodeType::SCALAR);
+                    TScalarType scalar_type;
+                    scalar_type.__set_type(TPrimitiveType::type::VARCHAR);
+                    scalar_type.__set_len(65535);
+                    node.__set_scalar_type(scalar_type);
+                    type.types.push_back(node);
+                }
+                slot_desc.slotType = type;
+                slot_desc.columnPos = i;
+                slot_desc.byteOffset = (i-1)*sizeof(StringValue) + 1*i;
+                slot_desc.nullIndicatorByte = 0+(i-1)*sizeof(StringValue) + 
1*(i-1);
+                slot_desc.nullIndicatorBit = 1;
+                std::ostringstream ss;
+                ss << "col_" << i;
+                slot_desc.colName = ss.str();
+                slot_desc.slotIdx = i;
+                slot_desc.isMaterialized = true;
+                SlotDescriptor* slot = new SlotDescriptor(slot_desc);
+                slot_descs.push_back(slot);
+            }
+            _slot_descs = &slot_descs;
+
+            std::vector<TabletColumn> col_schemas;
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_VARCHAR, true);
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_VARCHAR, true);
+            Schema schema(col_schemas, col_num);
+            _schema = &schema;
+
+            TupleRowZOrderComparator comparator(&schema, 2);
+            _comparator = comparator;
+
+            Tuple* lhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(StringValue), _buffer_mem_pool.get());
+            if (IS_FIRST_SLOT_NULL) {
+                lhs_tuple->set_null(NullIndicatorOffset(0, 1));
+            }
+            FillMem(lhs_tuple, 1, lval1, lval2);
+            uint8_t* lhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(StringValue));
+            ContiguousRow lhs_row(_schema, lhs_tuple_buf);
+            tuple_to_row(lhs_tuple, &lhs_row, _buffer_mem_pool.get());
+
+            Tuple* rhs_tuple = Tuple::create(col_num*sizeof(char) +  
col_num*sizeof(StringValue), _buffer_mem_pool.get());
+            FillMem(rhs_tuple, 1, rval1, rval2);
+            uint8_t* rhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(StringValue));
+            ContiguousRow rhs_row(_schema, rhs_tuple_buf);
+            tuple_to_row(rhs_tuple, &rhs_row, _buffer_mem_pool.get());
+            int result = _comparator.compare(reinterpret_cast<const 
char*>(lhs_row.row_ptr()),
+                                             reinterpret_cast<const 
char*>(rhs_row.row_ptr()));
+            return result;
+        }
+
+        template <typename T, bool IS_FIRST_SLOT_NULL = false>
+        int CompareDateTest(T lval1, T lval2, T rval1, T rval2) {
+            int col_num = 2;
+            std::vector<SlotDescriptor*> slot_descs;
+            for (int i = 1; i <= col_num; i++) {
+                TSlotDescriptor slot_desc;
+                slot_desc.id = i;
+                slot_desc.parent = 0;
+                TTypeDesc type;
+                {
+                    TTypeNode node;
+                    node.__set_type(TTypeNodeType::SCALAR);
+                    TScalarType scalar_type;
+                    scalar_type.__set_type(TPrimitiveType::type::DATETIME);
+                    node.__set_scalar_type(scalar_type);
+                    type.types.push_back(node);
+                }
+                slot_desc.slotType = type;
+                slot_desc.columnPos = i;
+                slot_desc.byteOffset = (i-1)*sizeof(DateTimeValue) + 1*i;
+                slot_desc.nullIndicatorByte = 0+(i-1)*sizeof(DateTimeValue) + 
1*(i-1);
+                slot_desc.nullIndicatorBit = 1;
+                std::ostringstream ss;
+                ss << "col_" << i;
+                slot_desc.colName = ss.str();
+                slot_desc.slotIdx = i;
+                slot_desc.isMaterialized = true;
+                SlotDescriptor* slot = new SlotDescriptor(slot_desc);
+                slot_descs.push_back(slot);
+            }
+            _slot_descs = &slot_descs;
+
+            std::vector<TabletColumn> col_schemas;
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_DATETIME, true);
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_DATETIME, true);
+            Schema schema(col_schemas, col_num);
+            _schema = &schema;
+
+            TupleRowZOrderComparator comparator(&schema, 2);
+            _comparator = comparator;
+
+            Tuple* lhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(DateTimeValue), _buffer_mem_pool.get());
+            if (IS_FIRST_SLOT_NULL) {
+                lhs_tuple->set_null(NullIndicatorOffset(0, 1));
+            }
+            FillMem(lhs_tuple, 1, lval1, lval2);
+            uint8_t* lhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + 
col_num*sizeof(DateTimeValue));
+            ContiguousRow lhs_row(_schema, lhs_tuple_buf);
+            tuple_to_row(lhs_tuple, &lhs_row, _buffer_mem_pool.get());
+
+            Tuple* rhs_tuple = Tuple::create(col_num*sizeof(char) +  
col_num*sizeof(DateTimeValue), _buffer_mem_pool.get());
+            FillMem(rhs_tuple, 1, rval1, rval2);
+            uint8_t* rhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + 
col_num*sizeof(DateTimeValue));
+            ContiguousRow rhs_row(_schema, rhs_tuple_buf);
+            tuple_to_row(rhs_tuple, &rhs_row, _buffer_mem_pool.get());
+            int result = _comparator.compare(reinterpret_cast<const 
char*>(lhs_row.row_ptr()),
+                                             reinterpret_cast<const 
char*>(rhs_row.row_ptr()));
+            return result;
+        }
+
+        template <typename T, bool IS_FIRST_SLOT_NULL = false>
+        int CompareDecimalTest(T lval1, T lval2, T rval1, T rval2) {
+            int col_num = 2;
+            std::vector<SlotDescriptor*> slot_descs;
+            for (int i = 1; i <= col_num; i++) {
+                TSlotDescriptor slot_desc;
+                slot_desc.id = i;
+                slot_desc.parent = 0;
+                TTypeDesc type;
+                {
+                    TTypeNode node;
+                    node.__set_type(TTypeNodeType::SCALAR);
+                    TScalarType scalar_type;
+                    scalar_type.__set_type(TPrimitiveType::type::DECIMALV2);
+                    scalar_type.__isset.precision = true;
+                    scalar_type.__isset.scale = true;
+                    scalar_type.__set_precision(-1);
+                    scalar_type.__set_scale(-1);
+                    node.__set_scalar_type(scalar_type);
+                    type.types.push_back(node);
+                }
+                slot_desc.slotType = type;
+                slot_desc.columnPos = i;
+                slot_desc.byteOffset = (i-1)*sizeof(int128_t) + 1*i;
+                slot_desc.nullIndicatorByte = 0+(i-1)*sizeof(int128_t) + 
1*(i-1);
+                slot_desc.nullIndicatorBit = 1;
+                std::ostringstream ss;
+                ss << "col_" << i;
+                slot_desc.colName = ss.str();
+                slot_desc.slotIdx = i;
+                slot_desc.isMaterialized = true;
+                SlotDescriptor* slot = new SlotDescriptor(slot_desc);
+                slot_descs.push_back(slot);
+            }
+            _slot_descs = &slot_descs;
+
+            std::vector<TabletColumn> col_schemas;
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_DECIMAL, true);
+            col_schemas.emplace_back(OLAP_FIELD_AGGREGATION_NONE, 
OLAP_FIELD_TYPE_DECIMAL, true);
+            Schema schema(col_schemas, col_num);
+            _schema = &schema;
+
+            TupleRowZOrderComparator comparator(&schema, 2);
+            _comparator = comparator;
+
+            Tuple* lhs_tuple = Tuple::create(col_num*sizeof(char) + 
col_num*sizeof(int128_t), _buffer_mem_pool.get());
+            if (IS_FIRST_SLOT_NULL) {
+                lhs_tuple->set_null(NullIndicatorOffset(0, 1));
+            }
+            FillMem(lhs_tuple, 1, lval1.value(), lval2.value());
+            uint8_t* lhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(int128_t));
+            ContiguousRow lhs_row(_schema, lhs_tuple_buf);
+            tuple_to_row(lhs_tuple, &lhs_row, _buffer_mem_pool.get());
+
+            Tuple* rhs_tuple = Tuple::create(col_num*sizeof(char) +  
col_num*sizeof(int128_t), _buffer_mem_pool.get());
+
+            FillMem(rhs_tuple, 1, rval1.value(), rval2.value());
+            uint8_t* rhs_tuple_buf = 
_buffer_mem_pool->allocate(col_num*sizeof(char) + col_num*sizeof(int128_t));
+            ContiguousRow rhs_row(_schema, rhs_tuple_buf);
+            tuple_to_row(rhs_tuple, &rhs_row, _buffer_mem_pool.get());
+            int result = _comparator.compare(reinterpret_cast<const 
char*>(lhs_row.row_ptr()),
+                                             reinterpret_cast<const 
char*>(rhs_row.row_ptr()));
+            return result;
+        }
+
+        void tuple_to_row(const Tuple* tuple, ContiguousRow* row, MemPool* 
mem_pool) {
+            for (size_t i = 0; i < _slot_descs->size(); ++i) {
+                auto cell = row->cell(i);
+                const SlotDescriptor* slot = (*_slot_descs)[i];
+                bool is_null = tuple->is_null(slot->null_indicator_offset());
+                const void* value = tuple->get_slot(slot->tuple_offset());
+                _schema->column(i)->consume(&cell, (const char*)value, 
is_null, mem_pool,
+                                            &_agg_buffer_pool);
+            }
+        }
+
+        template <typename T>
+        void FillMem(Tuple* tuple_mem, int idx, T val) {
+            memcpy(tuple_mem->get_slot(idx), &val, sizeof(T));
+        }
+
+        template <typename T, typename... Args>
+        void FillMem(Tuple* tuple_mem, int idx, T val, Args... args) {
+            // Use memcpy to avoid gcc generating unaligned instructions like 
movaps
+            // for int128_t. They will raise SegmentFault when addresses are 
not
+            // aligned to 16 bytes.
+            memcpy(tuple_mem->get_slot(idx), &val, sizeof(T));
+            FillMem(tuple_mem, idx + sizeof(T)+1, args...);
+        }
+
+        template <bool IS_FIRST_SLOT_NULL = false>
+        int Int8Int8Test(int8_t lval1, int8_t lval2, int8_t rval1, int8_t 
rval2) {
+            return CompareInt8Test<int8_t, IS_FIRST_SLOT_NULL>(lval1, lval2, 
rval1, rval2);
+        }
+
+        template <bool IS_FIRST_SLOT_NULL = false>
+        int Int16Int16Test(int16_t lval1, int16_t lval2, int16_t rval1, 
int16_t rval2) {
+            return CompareInt16Test<int16_t, IS_FIRST_SLOT_NULL>(lval1, lval2, 
rval1, rval2);
+        }
+
+        template <bool IS_FIRST_SLOT_NULL = false>
+        int IntIntTest(int32_t lval1, int32_t lval2, int32_t rval1, int32_t 
rval2) {
+            return CompareIntTest<int32_t, IS_FIRST_SLOT_NULL>(lval1, lval2, 
rval1, rval2);
+        }
+
+        template <bool IS_FIRST_SLOT_NULL = false>
+        int Int64Int64Test(int64_t lval1, int64_t lval2, int64_t rval1, 
int64_t rval2) {
+            return CompareInt64Test<int64_t, IS_FIRST_SLOT_NULL>(lval1, lval2, 
rval1, rval2);
+        }
+
+        template <bool IS_FIRST_SLOT_NULL = false>
+        int Int128Int128Test(int128_t lval1, int128_t lval2, int128_t rval1, 
int128_t rval2) {
+            return CompareInt128Test<int128_t, IS_FIRST_SLOT_NULL>(lval1, 
lval2, rval1, rval2);
+        }
+
+        template <bool IS_FIRST_SLOT_NULL = false>
+        int FloatFloatTest(float lval1, float lval2, float rval1, float rval2) 
{
+            return CompareFloatTest<float, IS_FIRST_SLOT_NULL>(lval1, lval2, 
rval1, rval2);
+        }
+
+        template <bool IS_FIRST_SLOT_NULL = false>
+        int DoubleDoubleTest(double lval1, double lval2, double rval1, double 
rval2) {
+            return CompareDoubleTest<double, IS_FIRST_SLOT_NULL>(lval1, lval2, 
rval1, rval2);
+        }
+
+        template <bool IS_FIRST_SLOT_NULL = false>
+        int BoolBoolTest(bool lval1, bool lval2, bool rval1, bool rval2) {
+            return CompareBoolTest<bool, IS_FIRST_SLOT_NULL>(lval1, lval2, 
rval1, rval2);
+        }
+
+        template <bool IS_FIRST_SLOT_NULL = false>
+        int CharCharTest(StringValue lval1, StringValue lval2, StringValue 
rval1, StringValue rval2) {
+            return CompareCharTest<StringValue, IS_FIRST_SLOT_NULL>(lval1, 
lval2, rval1, rval2);
+        }
+
+        template <bool IS_FIRST_SLOT_NULL = false>
+        int DateDateTest(DateTimeValue lval1, DateTimeValue lval2, 
DateTimeValue rval1, DateTimeValue rval2) {
+            return CompareDateTest<DateTimeValue, IS_FIRST_SLOT_NULL>(lval1, 
lval2, rval1, rval2);
+        }
+
+        template <bool IS_FIRST_SLOT_NULL = false>
+        int DecimalDecimalTest(DecimalV2Value lval1, DecimalV2Value lval2,
+                               DecimalV2Value rval1, DecimalV2Value rval2) {
+            return CompareDecimalTest<DecimalV2Value, 
IS_FIRST_SLOT_NULL>(lval1, lval2, rval1, rval2);
+        }
+    };
+
+TEST_F(TupleRowZOrderCompareTest, DecimalTest) {
+    std::string str1 = "1.00";
+    std::string str2 = "1.00";
+    std::string str3 = "1.00";
+    std::string str4 = "1.00";
+    DecimalV2Value val1(str1);
+    DecimalV2Value val2(str2);
+    DecimalV2Value val3(str3);
+    DecimalV2Value val4(str4);
+    EXPECT_EQ(DecimalDecimalTest(val1, val2, val3, val4), 0);
+    str1 = "-5.0";
+    str2 = "3.0";
+    str3 = "-5.0";
+    str4 = "3.0";
+    DecimalV2Value val5(str1);
+    DecimalV2Value val6(str2);
+    DecimalV2Value val7(str3);
+    DecimalV2Value val8(str4);
+    EXPECT_EQ(DecimalDecimalTest(val5, val6, val7, val8), 0);
+    str1 = "1.0";
+    str2 = "0.0";
+    str3 = "0.0";
+    str4 = "1.0";
+    DecimalV2Value val9(str1);
+    DecimalV2Value val10(str2);
+    DecimalV2Value val11(str3);
+    DecimalV2Value val12(str4);
+    EXPECT_EQ(DecimalDecimalTest(val9, val10, val11, val12), 1);
+    str1 = "0";
+    str2 = "1";
+    str3 = "1";
+    str4 = "0";
+    DecimalV2Value val13(str1);
+    DecimalV2Value val14(str2);
+    DecimalV2Value val15(str3);
+    DecimalV2Value val16(str4);
+    EXPECT_EQ(DecimalDecimalTest(val13, val14, val15, val16), -1);
+    str1 = "256";
+    str2 = "10";
+    str3 = "255";
+    str4 = "100";
+    DecimalV2Value val17(str1);
+    DecimalV2Value val18(str2);
+    DecimalV2Value val19(str3);
+    DecimalV2Value val20(str4);
+    EXPECT_EQ(DecimalDecimalTest(val17, val18, val19, val20), -1);
+    str1 = "3";
+    str2 = "1024";
+    str3 = "128";
+    str4 = "1023";
+    DecimalV2Value val21(str1);
+    DecimalV2Value val22(str2);
+    DecimalV2Value val23(str3);
+    DecimalV2Value val24(str4);
+    EXPECT_EQ(DecimalDecimalTest(val21, val22, val23, val24), -1);
+    str1 = "1024";
+    str2 = "511";
+    str3 = "1023";
+    str4 = "0";
+    DecimalV2Value val25(str1);
+    DecimalV2Value val26(str2);
+    DecimalV2Value val27(str3);
+    DecimalV2Value val28(str4);
+    EXPECT_EQ(DecimalDecimalTest(val25, val26, val27, val28), 1);
+    str1 = "5550";
+    str2 = "0";
+    str3 = "5000";
+    str4 = "4097";
+    DecimalV2Value val29(str1);
+    DecimalV2Value val30(str2);
+    DecimalV2Value val31(str3);
+    DecimalV2Value val32(str4);
+    EXPECT_EQ(DecimalDecimalTest(val29, val30, val31, val32), -1);
+}
+
+TEST_F(TupleRowZOrderCompareTest, DateDateTest) {
+    DateTimeValue val1;
+    DateTimeValue val2;
+    DateTimeValue val3;
+    DateTimeValue val4;
+    std::string str1 = "2015-04-09 14:07:46";
+    std::string str2 = "2015-04-09 14:07:46";
+    std::string str3 = "2015-04-09 14:07:46";
+    std::string str4 = "2015-04-09 14:07:46";
+    val1.from_date_str(str1.c_str(), str1.size());
+    val2.from_date_str(str2.c_str(), str2.size());
+    val3.from_date_str(str3.c_str(), str3.size());
+    val4.from_date_str(str4.c_str(), str4.size());
+    EXPECT_EQ(DateDateTest(val1, val2, val3, val4), 0);
+    str1 = "1415-12-09 10:07:44";
+    str2 = "2015-04-09 14:07:46";
+    str3 = "1415-12-09 10:07:44";
+    str4 = "2015-04-09 14:07:46";
+    val1.from_date_str(str1.c_str(), str1.size());
+    val2.from_date_str(str2.c_str(), str2.size());
+    val3.from_date_str(str3.c_str(), str3.size());
+    val4.from_date_str(str4.c_str(), str4.size());
+    EXPECT_EQ(DateDateTest(val1, val2, val3, val4), 0);
+    str1 = "1400-01-01 00:00:00";
+    str2 = "9999-12-31 14:07:46";
+    str3 = "8000-12-09 10:07:44";
+    str4 = "2015-04-09 14:07:46";
+    val1.from_date_str(str1.c_str(), str1.size());
+    val2.from_date_str(str2.c_str(), str2.size());
+    val3.from_date_str(str3.c_str(), str3.size());
+    val4.from_date_str(str4.c_str(), str4.size());
+    EXPECT_EQ(DateDateTest(val1, val2, val3, val4), -1);
+    str1 = "1400-01-01 00:00:01";
+    str2 = "1400-01-01 00:00:00";
+    str3 = "1400-01-01 00:00:00";
+    str4 = "1400-01-01 00:00:01";
+    val1.from_date_str(str1.c_str(), str1.size());
+    val2.from_date_str(str2.c_str(), str2.size());
+    val3.from_date_str(str3.c_str(), str3.size());
+    val4.from_date_str(str4.c_str(), str4.size());
+    EXPECT_EQ(DateDateTest(val1, val2, val3, val4), 1);
+    str1 = "1400-01-01 00:00:03";
+    str2 = "1400-01-01 00:00:07";
+    str3 = "1400-01-01 00:00:04";
+    str4 = "1400-01-01 00:00:00";
+    val1.from_date_str(str1.c_str(), str1.size());
+    val2.from_date_str(str2.c_str(), str2.size());
+    val3.from_date_str(str3.c_str(), str3.size());
+    val4.from_date_str(str4.c_str(), str4.size());
+    EXPECT_EQ(DateDateTest(val1, val2, val3, val4), -1);
+    str1 = "1400-01-01 00:00:06";
+    str2 = "1400-01-01 00:00:04";
+    str3 = "1400-01-01 00:00:05";
+    str4 = "1400-01-01 00:00:07";
+    val1.from_date_str(str1.c_str(), str1.size());
+    val2.from_date_str(str2.c_str(), str2.size());
+    val3.from_date_str(str3.c_str(), str3.size());
+    val4.from_date_str(str4.c_str(), str4.size());
+    EXPECT_EQ(DateDateTest(val1, val2, val3, val4), 1);
+    str1 = "1400-01-01 00:00:05";
+    str2 = "1400-01-01 00:00:05";
+    str3 = "1400-01-01 00:00:06";
+    str4 = "1400-01-01 00:00:04";
+    val1.from_date_str(str1.c_str(), str1.size());
+    val2.from_date_str(str2.c_str(), str2.size());
+    val3.from_date_str(str3.c_str(), str3.size());
+    val4.from_date_str(str4.c_str(), str4.size());
+    EXPECT_EQ(DateDateTest(val1, val2, val3, val4), -1);
+    str1 = "1400-01-01 23:59:59";
+    str2 = "1400-01-01 00:00:00";
+    str3 = "1400-01-02 00:00:00";
+    str4 = "1400-01-01 00:00:00";
+    val1.from_date_str(str1.c_str(), str1.size());
+    val2.from_date_str(str2.c_str(), str2.size());
+    val3.from_date_str(str3.c_str(), str3.size());
+    val4.from_date_str(str4.c_str(), str4.size());
+    EXPECT_EQ(DateDateTest(val1, val2, val3, val4), -1);
+    str1 = "3541-11-03 23:59:59";
+    str2 = "3541-11-03 00:00:00";
+    str3 = "3541-11-04 00:00:00";
+    str4 = "3541-11-03 00:00:00";
+    val1.from_date_str(str1.c_str(), str1.size());
+    val2.from_date_str(str2.c_str(), str2.size());
+    val3.from_date_str(str3.c_str(), str3.size());
+    val4.from_date_str(str4.c_str(), str4.size());
+    EXPECT_EQ(DateDateTest(val1, val2, val3, val4), -1);
+}
+TEST_F(TupleRowZOrderCompareTest, CharTest) {
+    EXPECT_EQ(CharCharTest(StringValue("a"), StringValue("b"), 
StringValue("a"), StringValue("b")), 0);
+    EXPECT_EQ(CharCharTest(StringValue("a"), StringValue("b"), 
StringValue("a"), StringValue("b")), 0);
+    EXPECT_EQ(CharCharTest(StringValue("h"), StringValue("0"), 
StringValue("h"), StringValue("0")), 0);
+    EXPECT_EQ(CharCharTest(StringValue("h"), StringValue("z"), 
StringValue("z"), StringValue("h")), -1);
+    EXPECT_EQ(CharCharTest(StringValue("a"), StringValue("0"), 
StringValue("h"), StringValue("0")), -1);
+    EXPECT_EQ(CharCharTest(StringValue("!"), StringValue("{"), 
StringValue("0"), StringValue("K")), 1);
+    EXPECT_EQ(CharCharTest(StringValue("A"), StringValue("~"), 
StringValue("B"), StringValue("Z")), 1);
+    EXPECT_EQ(CharCharTest(StringValue("aaa"), StringValue("bbb"), 
StringValue("aaa"), StringValue("bbb")), 0);
+    EXPECT_EQ(CharCharTest(StringValue("abc"), StringValue("bbc"), 
StringValue("abc"), StringValue("bbc")), 0);
+    EXPECT_EQ(CharCharTest(StringValue("aah"), StringValue("aa0"), 
StringValue("aah"), StringValue("aa0")), 0);
+    EXPECT_EQ(CharCharTest(StringValue("aaa"), StringValue("aa0"), 
StringValue("aah"), StringValue("aa0")), -1);
+    EXPECT_EQ(CharCharTest(StringValue("aah"), StringValue("aaz"), 
StringValue("aaz"), StringValue("aah")), -1);
+    EXPECT_EQ(CharCharTest(StringValue("aa!"), StringValue("aa{"), 
StringValue("aa0"), StringValue("aaK")), 1);
+    EXPECT_EQ(CharCharTest(StringValue("aaA"), StringValue("aa~"), 
StringValue("aaB"), StringValue("aaZ")), 1);
+}
+TEST_F(TupleRowZOrderCompareTest, BoolTest) {
+    EXPECT_EQ(BoolBoolTest(true, false, true, false), 0);
+    EXPECT_EQ(BoolBoolTest(false, true, false, true), 0);
+    EXPECT_EQ(BoolBoolTest(true, true, true, false), 1);
+    EXPECT_EQ(BoolBoolTest(false, true, true, true), -1);
+    EXPECT_EQ(BoolBoolTest(false, true, false, false), 1);
+    EXPECT_EQ(BoolBoolTest(false, false, false, true), -1);
+    EXPECT_EQ(BoolBoolTest(true, false, false, false), 1);
+}
+TEST_F(TupleRowZOrderCompareTest, DoubleTest) {
+    EXPECT_EQ(DoubleDoubleTest(1.0, 0.0, 0.0, 1.0f), 1);
+    EXPECT_EQ(DoubleDoubleTest(0.0, 1.0, 1.0, 0.0f), -1);
+    EXPECT_EQ(DoubleDoubleTest(4.0, 3.0, 3.0, 4.0), 1);
+    EXPECT_EQ(DoubleDoubleTest(5.0, 7.0, 4.0, 10.0), -1);
+    EXPECT_EQ(DoubleDoubleTest(6.0, 10.0, 7.0, 3.0), 1);
+    EXPECT_EQ(DoubleDoubleTest(9.0, 7.0, 8.0, 10.0), -1);
+    EXPECT_EQ(DoubleDoubleTest(8.0, 8.0, 9.0, 7.0), 1);
+    EXPECT_EQ(DoubleDoubleTest(9.0, 4.0, 6.0, 10.0), 1);
+    EXPECT_EQ(DoubleDoubleTest(-4.0, -3.0, -3.0, -4.0), -1);
+    EXPECT_EQ(DoubleDoubleTest(-5.0, -7.0, -4.0, -10.0), 1);
+    EXPECT_EQ(DoubleDoubleTest(-6.0, -10.0, -7.0, -3.0), -1);
+    EXPECT_EQ(DoubleDoubleTest(-9.0, -7.0, -8.0, -10.0), 1);
+    EXPECT_EQ(DoubleDoubleTest(-8.0, -8.0, -9.0, -7.0), -1);
+    EXPECT_EQ(DoubleDoubleTest(-9.0, -4.0, -6.0, -10.0), -1);
+    EXPECT_EQ(DoubleDoubleTest(DBL_MAX / 2.0 + 2.0, 1.0, 1.0, DBL_MAX), 1);
+}
+TEST_F(TupleRowZOrderCompareTest, FloatTest) {
+    EXPECT_EQ(FloatFloatTest(1.0f, 0.0f, 0.0f, 1.0f), 1);
+    EXPECT_EQ(FloatFloatTest(0.0f, 1.0f, 1.0f, 0.0f), -1);
+    EXPECT_EQ(FloatFloatTest(4.0f, 3.0f, 3.0f, 4.0f), 1);
+    EXPECT_EQ(FloatFloatTest(5.0f, 7.0f, 4.0f, 10.0f), -1);
+    EXPECT_EQ(FloatFloatTest(6.0f, 10.0f, 7.0f, 3.0f), 1);
+    EXPECT_EQ(FloatFloatTest(9.0f, 7.0f, 8.0f, 10.0f), -1);
+    EXPECT_EQ(FloatFloatTest(8.0f , 8.0f, 9.0f, 7.0f), 1);
+    EXPECT_EQ(FloatFloatTest(9.0f, 4.0f, 6.0f, 10.0f), 1);
+    EXPECT_EQ(FloatFloatTest(-4.0f, -3.0f, -3.0f, -4.0f), -1);
+    EXPECT_EQ(FloatFloatTest(-5.0f, -7.0f, -4.0f, -10.0f), 1);
+    EXPECT_EQ(FloatFloatTest(-6.0f, -10.0f, -7.0f, -3.0f), -1);
+    EXPECT_EQ(FloatFloatTest(-9.0f, -7.0f, -8.0f, -10.0f), 1);
+    EXPECT_EQ(FloatFloatTest(-8.0f, -8.0f, -9.0f, -7.0f), -1);
+    EXPECT_EQ(FloatFloatTest(-9.0f, -4.0f, -6.0f, -10.0f), -1);
+    EXPECT_EQ(FloatFloatTest(FLT_MAX / 2.0f + 2.0f, 1.0f, 1.0f, FLT_MAX), 1);
+}
+TEST_F(TupleRowZOrderCompareTest, Int8Test) {
+    EXPECT_EQ(Int8Int8Test(0, 0, 0, 0), 0);
+    EXPECT_EQ(Int8Int8Test(-5, 3, -5, 3), 0);
+    EXPECT_EQ(Int8Int8Test(1, 0, 0, 1), 1);
+    EXPECT_EQ(Int8Int8Test(0, 1, 1, 0), -1);
+    EXPECT_EQ(Int8Int8Test(1, 0, 0, 1), 1);
+    EXPECT_EQ(Int8Int8Test(2, 4, 1, 7), 1);
+    EXPECT_EQ(Int8Int8Test(3, 7, 4, 0), -1);
+    EXPECT_EQ(Int8Int8Test(6, 4, 5, 7), 1);
+    EXPECT_EQ(Int8Int8Test(5, 5, 6, 4), -1);
+    EXPECT_EQ(Int8Int8Test(6, 1, 3, 7), 1);
+    EXPECT_EQ(Int8Int8Test(INT8_MAX / 2 + 2, 1, 1, INT8_MAX), 1);
+    EXPECT_EQ(Int8Int8Test(INT8_MAX / 2, 1, 1, INT8_MAX), -1);
+}
+TEST_F(TupleRowZOrderCompareTest, Int16Test) {
+    EXPECT_EQ(Int16Int16Test(0, 0, 0, 0), 0);
+    EXPECT_EQ(Int16Int16Test(-5, 3, -5, 3), 0);
+    EXPECT_EQ(Int16Int16Test(1, 0, 0, 1), 1);
+    EXPECT_EQ(Int16Int16Test(0, 1, 1, 0), -1);
+    EXPECT_EQ(Int16Int16Test(1, 0, 0, 1), 1);
+    EXPECT_EQ(Int16Int16Test(2, 4, 1, 7), 1);
+    EXPECT_EQ(Int16Int16Test(3, 7, 4, 0), -1);
+    EXPECT_EQ(Int16Int16Test(6, 4, 5, 7), 1);
+    EXPECT_EQ(Int16Int16Test(5, 5, 6, 4), -1);
+    EXPECT_EQ(Int16Int16Test(6, 1, 3, 7), 1);
+    EXPECT_EQ(Int16Int16Test(INT16_MAX / 2 + 2, 1, 1, INT16_MAX), 1);
+    EXPECT_EQ(Int16Int16Test(INT16_MAX / 2, 1, 1, INT16_MAX), -1);
+}
+
+TEST_F(TupleRowZOrderCompareTest, Int32Test) {
+    EXPECT_EQ(IntIntTest(0, 2, 1, 1), 1);
+    EXPECT_EQ(IntIntTest(-5, 3, -5, 3), 0);
+
+    EXPECT_EQ(IntIntTest(0, 4, 1, 2), 1);
+    EXPECT_EQ(IntIntTest(0, 1, 1, 0), -1);
+
+    EXPECT_EQ(IntIntTest(1, 0, 0, 1), 1);
+    EXPECT_EQ(IntIntTest(2, 4, 1, 7), 1);
+    EXPECT_EQ(IntIntTest(3, 7, 4, 0), -1);
+    EXPECT_EQ(IntIntTest(6, 4, 5, 7), 1);
+    EXPECT_EQ(IntIntTest(5, 5, 6, 4), -1);
+    EXPECT_EQ(IntIntTest(6, 1, 3, 7), 1);
+
+    EXPECT_EQ(IntIntTest(INT32_MAX / 2 + 2, 1, 1, INT32_MAX), 1);
+    EXPECT_EQ(IntIntTest(INT32_MAX / 2, 1, 1, INT32_MAX), -1);
+
+    EXPECT_EQ(IntIntTest<true>(1, 1, 1, 1), -1);
+    EXPECT_EQ(IntIntTest<true>(4242, 1, 1, 1), -1);
+    EXPECT_EQ(IntIntTest<true>(1, 0, 0, 1), -1);
+    EXPECT_EQ(IntIntTest<true>(1, 0, INT32_MIN, 0), 0);
+}
+
+TEST_F(TupleRowZOrderCompareTest, Int64Test) {
+    EXPECT_EQ(Int64Int64Test(0, 0, 0, 0), 0);
+    EXPECT_EQ(Int64Int64Test(-5, 3, -5, 3), 0);
+    EXPECT_EQ(Int64Int64Test(1, 0, 0, 1), 1);
+    EXPECT_EQ(Int64Int64Test(0, 1, 1, 0), -1);
+    EXPECT_EQ(Int64Int64Test(1, 0, 0, 1), 1);
+    EXPECT_EQ(Int64Int64Test(2, 4, 1, 7), 1);
+    EXPECT_EQ(Int64Int64Test(3, 7, 4, 0), -1);
+    EXPECT_EQ(Int64Int64Test(6, 4, 5, 7), 1);
+    EXPECT_EQ(Int64Int64Test(5, 5, 6, 4), -1);
+    EXPECT_EQ(Int64Int64Test(6, 1, 3, 7), 1);
+    EXPECT_EQ(Int64Int64Test(INT64_MAX / 2 + 2, 1, 1, INT64_MAX), 1);
+    EXPECT_EQ(Int64Int64Test(INT64_MAX / 2, 1, 1, INT64_MAX), -1);
+}
+TEST_F(TupleRowZOrderCompareTest, LargeIntTest) {
+    EXPECT_EQ(Int128Int128Test(0, 0, 0, 0), 0);
+    EXPECT_EQ(Int128Int128Test(-5, 3, -5, 3), 0);
+    EXPECT_EQ(Int128Int128Test(1, 0, 0, 1), 1);
+    EXPECT_EQ(Int128Int128Test(0, 1, 1, 0), -1);
+    EXPECT_EQ(Int128Int128Test(1, 0, 0, 1), 1);
+    EXPECT_EQ(Int128Int128Test(2, 4, 1, 7), 1);
+    EXPECT_EQ(Int128Int128Test(3, 7, 4, 0), -1);
+    EXPECT_EQ(Int128Int128Test(6, 4, 5, 7), 1);
+    EXPECT_EQ(Int128Int128Test(5, 5, 6, 4), -1);
+    EXPECT_EQ(Int128Int128Test(6, 1, 3, 7), 1);
+    EXPECT_EQ(Int128Int128Test(MAX_INT128 / 2 + 2, 1, 1, MAX_INT128), 1);
+    Int128Int128Test(MAX_INT128 / 2, 1, 1, MAX_INT128);
+}
+
+} // namespace doris
+
+int main(int argc, char** argv) {
+    std::string conffile = std::string(getenv("DORIS_HOME")) + "/conf/be.conf";
+    if (!doris::config::init(conffile.c_str(), false)) {
+        fprintf(stderr, "error read config file. \n");
+        return -1;
+    }
+    doris::init_glog("be-test");
+    ::testing::InitGoogleTest(&argc, argv);
+    doris::CpuInfo::init();
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git 
a/docs/en/sql-reference-v2/sql-statements/Data-Definition-Statements/Create/CREATE-TABLE.md
 
b/docs/en/sql-reference-v2/sql-statements/Data-Definition-Statements/Create/CREATE-TABLE.md
index 156fc51..72916ef 100644
--- 
a/docs/en/sql-reference-v2/sql-statements/Data-Definition-Statements/Create/CREATE-TABLE.md
+++ 
b/docs/en/sql-reference-v2/sql-statements/Data-Definition-Statements/Create/CREATE-TABLE.md
@@ -297,6 +297,13 @@ distribution_info
         * `dynamic_partition.history_partition_num`: Specify the number of 
historical partitions to be created.
         * `dynamic_partition.reserved_history_periods`: Used to specify the 
range of reserved history periods.
 
+    * Data Sort Info
+        
+        The relevant parameters of data sort info are as follows:
+        
+        * `data_sort.sort_type`: the method of data sorting, options: 
z-order/lexical, default is lexical
+        * `data_sort.col_num`:  the first few columns to sort, col_num muster 
less than total key counts
+      
 ### Example
 
 1. Create a detailed model table
diff --git 
a/docs/zh-CN/sql-reference-v2/sql-statements/Data-Definition-Statements/Create/CREATE-TABLE.md
 
b/docs/zh-CN/sql-reference-v2/sql-statements/Data-Definition-Statements/Create/CREATE-TABLE.md
index b240f0a..1b0229c 100644
--- 
a/docs/zh-CN/sql-reference-v2/sql-statements/Data-Definition-Statements/Create/CREATE-TABLE.md
+++ 
b/docs/zh-CN/sql-reference-v2/sql-statements/Data-Definition-Statements/Create/CREATE-TABLE.md
@@ -298,6 +298,12 @@ distribution_info
         * `dynamic_partition.history_partition_num`: 指定创建历史分区的数量。
         * `dynamic_partition.reserved_history_periods`: 用于指定保留的历史分区的时间段。
 
+    * 数据排序相关
+
+        数据排序相关参数如下:
+
+        * `data_sort.sort_type`: 数据排序使用的方法,目前支持两种:lexical/z-order,默认是lexical
+        * `data_sort.col_num`: 数据排序使用的列数,取最前面几列,不能超过总的key 列数
 ### Example
 
 1. 创建一个明细模型的表
diff --git a/fe/fe-core/src/main/java/org/apache/doris/alter/Alter.java 
b/fe/fe-core/src/main/java/org/apache/doris/alter/Alter.java
index ee8a049..85eaaaf 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/alter/Alter.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/alter/Alter.java
@@ -66,6 +66,7 @@ import org.apache.doris.persist.ModifyTableEngineOperationLog;
 import org.apache.doris.persist.ReplaceTableOperationLog;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.thrift.TOdbcTableType;
+import org.apache.doris.thrift.TSortType;
 import org.apache.doris.thrift.TTabletType;
 
 import com.google.common.base.Preconditions;
@@ -126,6 +127,10 @@ public class Alter {
 
     private boolean processAlterOlapTable(AlterTableStmt stmt, OlapTable 
olapTable, List<AlterClause> alterClauses,
                                          final String clusterName, Database 
db) throws UserException {
+        if (olapTable.getDataSortInfo() != null
+                && olapTable.getDataSortInfo().getSortType() == 
TSortType.ZORDER) {
+            throw new UserException("z-order table can not support schema 
change!");
+        }
         stmt.rewriteAlterClause(olapTable);
 
         // check conflict alter ops first
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/DataSortInfo.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/DataSortInfo.java
new file mode 100644
index 0000000..5b33d25
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/DataSortInfo.java
@@ -0,0 +1,103 @@
+// 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.
+
+package org.apache.doris.analysis;
+
+import com.google.gson.annotations.SerializedName;
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.io.Writable;
+import org.apache.doris.persist.gson.GsonUtils;
+import org.apache.doris.thrift.TSortType;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Map;
+
+public class DataSortInfo implements Writable {
+    public static final String DATA_SORT_PROPERTY_PREFIX = "data_sort";
+    public static final String DATA_SORT_TYPE = "data_sort.sort_type";
+    public static final String DATA_SORT_COL_NUM = "data_sort.col_num";
+
+    @SerializedName(value = "sort_type")
+    private TSortType sortType;
+    @SerializedName(value = "col_num")
+    private int colNum;
+
+    public DataSortInfo() {
+
+    }
+
+    public DataSortInfo(Map<String, String> properties) {
+        if (properties != null && !properties.isEmpty()) {
+            if (properties.get(DATA_SORT_TYPE).equalsIgnoreCase("ZORDER")) {
+                this.sortType = TSortType.ZORDER;
+            } else {
+                this.sortType = TSortType.LEXICAL;
+            }
+            this.colNum = Integer.parseInt(properties.get(DATA_SORT_COL_NUM));
+        }
+    }
+
+    public DataSortInfo (TSortType sortType, int colNum) {
+        this.sortType = sortType;
+        this.colNum = colNum;
+    }
+
+    public TSortType getSortType() {
+        return sortType;
+    }
+
+    public void setSortType(TSortType sortType) {
+        this.sortType = sortType;
+    }
+
+    public int getColNum() {
+        return colNum;
+    }
+
+    public void setColNum(int colNum) {
+        this.colNum = colNum;
+    }
+
+    @Override
+    public void write(DataOutput out) throws IOException {
+        String json = GsonUtils.GSON.toJson(this);
+        Text.writeString(out, json);
+    }
+
+    public static DataSortInfo read(DataInput in) throws IOException {
+        String json = Text.readString(in);
+        return GsonUtils.GSON.fromJson(json, DataSortInfo.class);
+    }
+
+    public boolean equals(DataSortInfo dataSortInfo) {
+        if (this.sortType != dataSortInfo.sortType) {
+            return false;
+        }
+        if (this.colNum != dataSortInfo.colNum) {
+            return false;
+        }
+        return true;
+    }
+
+    public String toSql() {
+        String res = ",\n\"" + DATA_SORT_TYPE + "\" = \"" + this.sortType + 
"\"" +
+                ",\n\"" + DATA_SORT_COL_NUM + "\" = \"" + this.colNum + "\"";
+        return res;
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java
index b56393b..b2cf59d 100755
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java
@@ -93,6 +93,7 @@ import org.apache.doris.analysis.TypeDef;
 import org.apache.doris.analysis.UninstallPluginStmt;
 import org.apache.doris.analysis.UserDesc;
 import org.apache.doris.analysis.UserIdentity;
+import org.apache.doris.analysis.DataSortInfo;
 import org.apache.doris.backup.BackupHandler;
 import org.apache.doris.blockrule.SqlBlockRuleMgr;
 import org.apache.doris.catalog.ColocateTableIndex.GroupId;
@@ -3254,7 +3255,6 @@ public class Catalog {
         // create partition outside db lock
         DataProperty dataProperty = 
singlePartitionDesc.getPartitionDataProperty();
         Preconditions.checkNotNull(dataProperty);
-
         // check replica quota if this operation done
         long indexNum = indexIdToMeta.size();
         long bucketNum = distributionInfo.getBucketNum();
@@ -3281,7 +3281,8 @@ public class Catalog {
                     tabletIdSet, olapTable.getCopiedIndexes(),
                     singlePartitionDesc.isInMemory(),
                     olapTable.getStorageFormat(),
-                    singlePartitionDesc.getTabletType()
+                    singlePartitionDesc.getTabletType(),
+                    olapTable.getDataSortInfo()
             );
 
             // check again
@@ -3512,7 +3513,8 @@ public class Catalog {
                                                  List<Index> indexes,
                                                  boolean isInMemory,
                                                  TStorageFormat storageFormat,
-                                                 TTabletType tabletType) 
throws DdlException {
+                                                 TTabletType tabletType,
+                                                 DataSortInfo 
dataSortInfo)throws DdlException {
         // create base index first.
         Preconditions.checkArgument(baseIndexId != -1);
         MaterializedIndex baseIndex = new MaterializedIndex(baseIndexId, 
IndexState.NORMAL);
@@ -3579,7 +3581,8 @@ public class Catalog {
                             countDownLatch,
                             indexes,
                             isInMemory,
-                            tabletType);
+                            tabletType,
+                            dataSortInfo);
                     task.setStorageFormat(storageFormat);
                     batchTask.addTask(task);
                     // add to AgentTaskQueue for handling finish report.
@@ -3688,6 +3691,20 @@ public class Catalog {
         // this should be done before create partition.
         Map<String, String> properties = stmt.getProperties();
 
+        // get storage format
+        TStorageFormat storageFormat = TStorageFormat.V2; // default is 
segment v2
+        try {
+            storageFormat = PropertyAnalyzer.analyzeStorageFormat(properties);
+        } catch (AnalysisException e) {
+            throw new DdlException(e.getMessage());
+        }
+        olapTable.setStorageFormat(storageFormat);
+
+        // check data sort properties
+        DataSortInfo dataSortInfo = 
PropertyAnalyzer.analyzeDataSortInfo(properties, keysType,
+                keysDesc.keysColumnSize(), storageFormat);
+        olapTable.setDataSortInfo(dataSortInfo);
+
         // analyze bloom filter columns
         Set<String> bfColumns = null;
         double bfFpp = 0;
@@ -3828,15 +3845,6 @@ public class Catalog {
         }
         Preconditions.checkNotNull(versionInfo);
 
-        // get storage format
-        TStorageFormat storageFormat = TStorageFormat.V2; // default is 
segment v2
-        try {
-            storageFormat = PropertyAnalyzer.analyzeStorageFormat(properties);
-        } catch (AnalysisException e) {
-            throw new DdlException(e.getMessage());
-        }
-        olapTable.setStorageFormat(storageFormat);
-
         // a set to record every new tablet created when create table
         // if failed in any step, use this set to do clear things
         Set<Long> tabletIdSet = new HashSet<>();
@@ -3868,7 +3876,7 @@ public class Catalog {
                         partitionInfo.getReplicaAllocation(partitionId),
                         versionInfo, bfColumns, bfFpp,
                         tabletIdSet, olapTable.getCopiedIndexes(),
-                        isInMemory, storageFormat, tabletType);
+                        isInMemory, storageFormat, tabletType, 
olapTable.getDataSortInfo());
                 olapTable.addPartition(partition);
             } else if (partitionInfo.getType() == PartitionType.RANGE || 
partitionInfo.getType() == PartitionType.LIST) {
                 try {
@@ -3918,7 +3926,7 @@ public class Catalog {
                             versionInfo, bfColumns, bfFpp,
                             tabletIdSet, olapTable.getCopiedIndexes(),
                             isInMemory, storageFormat,
-                            partitionInfo.getTabletType(entry.getValue()));
+                            partitionInfo.getTabletType(entry.getValue()), 
olapTable.getDataSortInfo());
                     olapTable.addPartition(partition);
                 }
             } else {
@@ -4235,6 +4243,11 @@ public class Catalog {
                 
sb.append(olapTable.getTableProperty().getDynamicPartitionProperty().getProperties(replicaAlloc));
             }
 
+            // only display z-order sort info
+            if (olapTable.isZOrderSort()) {
+                sb.append(olapTable.getDataSortInfo().toSql());
+            }
+
             // in memory
             
sb.append(",\n\"").append(PropertyAnalyzer.PROPERTIES_INMEMORY).append("\" = 
\"");
             sb.append(olapTable.isInMemory()).append("\"");
@@ -6761,7 +6774,8 @@ public class Catalog {
                         copiedTbl.getCopiedIndexes(),
                         copiedTbl.isInMemory(),
                         copiedTbl.getStorageFormat(),
-                        
copiedTbl.getPartitionInfo().getTabletType(oldPartitionId));
+                        
copiedTbl.getPartitionInfo().getTabletType(oldPartitionId),
+                        copiedTbl.getDataSortInfo());
                 newPartitions.add(newPartition);
             }
         } catch (DdlException e) {
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 5fde029..73fa24f 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
@@ -24,6 +24,7 @@ import org.apache.doris.analysis.CreateTableStmt;
 import org.apache.doris.analysis.Expr;
 import org.apache.doris.analysis.SlotDescriptor;
 import org.apache.doris.analysis.SlotRef;
+import org.apache.doris.analysis.DataSortInfo;
 import org.apache.doris.backup.Status;
 import org.apache.doris.backup.Status.ErrCode;
 import org.apache.doris.catalog.DistributionInfo.DistributionInfoType;
@@ -54,6 +55,7 @@ import org.apache.doris.thrift.TStorageMedium;
 import org.apache.doris.thrift.TStorageType;
 import org.apache.doris.thrift.TTableDescriptor;
 import org.apache.doris.thrift.TTableType;
+import org.apache.doris.thrift.TSortType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
@@ -197,6 +199,12 @@ public class OlapTable extends Table {
                 && tableProperty.getDynamicPartitionProperty().isExist();
     }
 
+    public boolean isZOrderSort() {
+        return tableProperty != null
+                && tableProperty.getDataSortInfo() != null
+                && tableProperty.getDataSortInfo().getSortType() == 
TSortType.ZORDER;
+    }
+
     public void setBaseIndexId(long baseIndexId) {
         this.baseIndexId = baseIndexId;
     }
@@ -1263,7 +1271,6 @@ public class OlapTable extends Table {
                 tempPartitions.unsetPartitionInfo();
             }
         }
-
         // In the present, the fullSchema could be rebuilt by schema change 
while the properties is changed by MV.
         // After that, some properties of fullSchema and nameToColumn may be 
not same as properties of base columns.
         // So, here we need to rebuild the fullSchema to ensure the 
correctness of the properties.
@@ -1569,6 +1576,14 @@ public class OlapTable extends Table {
         tableProperty.buildInMemory();
     }
 
+    public void setDataSortInfo(DataSortInfo dataSortInfo) {
+        if (tableProperty == null) {
+            tableProperty = new TableProperty(new HashMap<>());
+        }
+        tableProperty.modifyDataSortInfoProperties(dataSortInfo);
+        tableProperty.buildDataSortInfo();
+    }
+
     // return true if partition with given name already exist, both in 
partitions and temp partitions.
     // return false otherwise
     public boolean checkPartitionNameExist(String partitionName) {
@@ -1714,6 +1729,13 @@ public class OlapTable extends Table {
         return tableProperty.getStorageFormat();
     }
 
+    public DataSortInfo getDataSortInfo() {
+        if (tableProperty == null) {
+            return new DataSortInfo(TSortType.LEXICAL, this.getKeysNum());
+        }
+        return tableProperty.getDataSortInfo();
+    }
+
     // For non partitioned table:
     //   The table's distribute hash columns need to be a subset of the 
aggregate columns.
     //
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 dcbaeba..7d1c4ca 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
@@ -17,6 +17,7 @@
 
 package org.apache.doris.catalog;
 
+import org.apache.doris.analysis.DataSortInfo;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.Config;
 import org.apache.doris.common.DdlException;
@@ -69,6 +70,8 @@ public class TableProperty implements Writable {
      */
     private TStorageFormat storageFormat = TStorageFormat.DEFAULT;
 
+    private DataSortInfo dataSortInfo = new DataSortInfo();
+
     public TableProperty(Map<String, String> properties) {
         this.properties = properties;
     }
@@ -127,6 +130,17 @@ public class TableProperty implements Writable {
         return this;
     }
 
+    public TableProperty buildDataSortInfo() {
+        HashMap<String, String> dataSortInfoProperties = new HashMap<>();
+        for (Map.Entry<String, String> entry : properties.entrySet()) {
+            if 
(entry.getKey().startsWith(DataSortInfo.DATA_SORT_PROPERTY_PREFIX)) {
+                dataSortInfoProperties.put(entry.getKey(), entry.getValue());
+            }
+        }
+        dataSortInfo = new DataSortInfo(dataSortInfoProperties);
+        return this;
+    }
+
     public TableProperty buildStorageFormat() {
         storageFormat = 
TStorageFormat.valueOf(properties.getOrDefault(PropertyAnalyzer.PROPERTIES_STORAGE_FORMAT,
                 TStorageFormat.DEFAULT.name()));
@@ -137,6 +151,11 @@ public class TableProperty implements Writable {
         properties.putAll(modifyProperties);
     }
 
+    public void modifyDataSortInfoProperties(DataSortInfo dataSortInfo) {
+        properties.put(DataSortInfo.DATA_SORT_TYPE, 
String.valueOf(dataSortInfo.getSortType()));
+        properties.put(DataSortInfo.DATA_SORT_COL_NUM, 
String.valueOf(dataSortInfo.getColNum()));
+    }
+
     public void setReplicaAlloc(ReplicaAllocation replicaAlloc) {
         this.replicaAlloc = replicaAlloc;
         // set it to "properties" so that this info can be persisted
@@ -178,6 +197,10 @@ public class TableProperty implements Writable {
         return storageFormat;
     }
 
+    public DataSortInfo getDataSortInfo() {
+        return dataSortInfo;
+    }
+
     public void buildReplicaAllocation() {
         try {
             // Must copy the properties because "analyzeReplicaAllocation" 
with remove the property
@@ -200,7 +223,8 @@ public class TableProperty implements Writable {
         TableProperty tableProperty = 
GsonUtils.GSON.fromJson(Text.readString(in), TableProperty.class)
                 .executeBuildDynamicProperty()
                 .buildInMemory()
-                .buildStorageFormat();
+                .buildStorageFormat()
+                .buildDataSortInfo();
         if (Catalog.getCurrentCatalogJournalVersion() < 
FeMetaVersion.VERSION_105) {
             // get replica num from property map and create replica allocation
             String repNum = 
tableProperty.properties.remove(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM);
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 29e7c7f..798b4d3 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
@@ -17,6 +17,7 @@
 
 package org.apache.doris.common.util;
 
+import org.apache.doris.analysis.DataSortInfo;
 import org.apache.doris.analysis.DateLiteral;
 import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.DataProperty;
@@ -34,6 +35,7 @@ import org.apache.doris.thrift.TStorageFormat;
 import org.apache.doris.thrift.TStorageMedium;
 import org.apache.doris.thrift.TStorageType;
 import org.apache.doris.thrift.TTabletType;
+import org.apache.doris.thrift.TSortType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
@@ -534,4 +536,43 @@ public class PropertyAnalyzer {
         }
         return replicaAlloc;
     }
+
+    public static DataSortInfo analyzeDataSortInfo(Map<String, String> 
properties, KeysType keyType,
+                                                   int keyCount, 
TStorageFormat storageFormat) throws AnalysisException {
+        if (properties == null || properties.isEmpty()) {
+            return new DataSortInfo(TSortType.LEXICAL, keyCount);
+        }
+        String sortMethod = TSortType.LEXICAL.name();
+        if (properties.containsKey(DataSortInfo.DATA_SORT_TYPE)) {
+            sortMethod = properties.remove(DataSortInfo.DATA_SORT_TYPE);
+        }
+        TSortType sortType = TSortType.LEXICAL;
+        if (sortMethod.equalsIgnoreCase(TSortType.ZORDER.name())) {
+            sortType = TSortType.ZORDER;
+        } else if (sortMethod.equalsIgnoreCase(TSortType.LEXICAL.name())) {
+            sortType = TSortType.LEXICAL;
+        } else {
+            throw new AnalysisException("only support zorder/lexical method!");
+        }
+        if (keyType != KeysType.DUP_KEYS && sortType == TSortType.ZORDER) {
+            throw new AnalysisException("only duplicate key supports zorder 
method!");
+        }
+        if (storageFormat != TStorageFormat.V2 && sortType == 
TSortType.ZORDER) {
+            throw new AnalysisException("only V2 storage format supports 
zorder method!");
+        }
+
+        int colNum = keyCount;
+        if (properties.containsKey(DataSortInfo.DATA_SORT_COL_NUM)) {
+            try {
+                colNum = 
Integer.valueOf(properties.remove(DataSortInfo.DATA_SORT_COL_NUM));
+            } catch (Exception e) {
+                throw new AnalysisException("param " + 
DataSortInfo.DATA_SORT_COL_NUM + " error");
+            }
+        }
+        if (sortType == TSortType.ZORDER && (colNum <= 1 || colNum > 
keyCount)) {
+            throw new AnalysisException("z-order needs 2 columns at least, " + 
keyCount + " columns at most!");
+        }
+        DataSortInfo dataSortInfo = new DataSortInfo(sortType, colNum);
+        return dataSortInfo;
+    }
 }
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 04055d7..858f7dd 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
@@ -18,6 +18,7 @@
 package org.apache.doris.task;
 
 import org.apache.doris.alter.SchemaChangeHandler;
+import org.apache.doris.analysis.DataSortInfo;
 import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.Index;
 import org.apache.doris.catalog.KeysType;
@@ -82,6 +83,8 @@ public class CreateReplicaTask extends AgentTask {
     // true if this task is created by recover request(See comment of 
Config.recover_with_empty_tablet)
     private boolean isRecoverTask = false;
 
+    private DataSortInfo dataSortInfo;
+
     public CreateReplicaTask(long backendId, long dbId, long tableId, long 
partitionId, long indexId, long tabletId,
                              short shortKeyColumnCount, int schemaHash, long 
version, long versionHash,
                              KeysType keysType, TStorageType storageType,
@@ -114,6 +117,40 @@ public class CreateReplicaTask extends AgentTask {
         this.tabletType = tabletType;
     }
 
+    public CreateReplicaTask(long backendId, long dbId, long tableId, long 
partitionId, long indexId, long tabletId,
+                             short shortKeyColumnCount, int schemaHash, long 
version, long versionHash,
+                             KeysType keysType, TStorageType storageType,
+                             TStorageMedium storageMedium, List<Column> 
columns,
+                             Set<String> bfColumns, double bfFpp, 
MarkedCountDownLatch<Long, Long> latch,
+                             List<Index> indexes,
+                             boolean isInMemory,
+                             TTabletType tabletType,
+                             DataSortInfo dataSortInfo) {
+        super(null, backendId, TTaskType.CREATE, dbId, tableId, partitionId, 
indexId, tabletId);
+
+        this.shortKeyColumnCount = shortKeyColumnCount;
+        this.schemaHash = schemaHash;
+
+        this.version = version;
+        this.versionHash = versionHash;
+
+        this.keysType = keysType;
+        this.storageType = storageType;
+        this.storageMedium = storageMedium;
+
+        this.columns = columns;
+
+        this.bfColumns = bfColumns;
+        this.indexes = indexes;
+        this.bfFpp = bfFpp;
+
+        this.latch = latch;
+
+        this.isInMemory = isInMemory;
+        this.tabletType = tabletType;
+        this.dataSortInfo = dataSortInfo;
+    }
+
     public void setIsRecoverTask(boolean isRecoverTask) {
         this.isRecoverTask = isRecoverTask;
     }
@@ -165,6 +202,10 @@ public class CreateReplicaTask extends AgentTask {
         tSchema.setSchemaHash(schemaHash);
         tSchema.setKeysType(keysType.toThrift());
         tSchema.setStorageType(storageType);
+        if (dataSortInfo != null) {
+            tSchema.setSortType(dataSortInfo.getSortType());
+            tSchema.setSortColNum(dataSortInfo.getColNum());
+        }
         int deleteSign = -1;
         int sequenceCol = -1;
         List<TColumn> tColumns = new ArrayList<TColumn>();
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateTableTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateTableTest.java
index 4db3053..24b3c89 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateTableTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateTableTest.java
@@ -478,4 +478,45 @@ public class CreateTableTest {
                                 ");"));
 
     }
+
+    @Test
+    public void testZOrderTable() {
+        // create lexically sort table
+        ExceptionChecker.expectThrowsNoException(() -> createTable(
+                "create table test.zorder_tbl1\n" + "(k1 varchar(40), k2 int, 
k3 int)\n" + "duplicate key(k1, k2, k3)\n"
+                        + "partition by range(k2)\n" + "(partition p1 values 
less than(\"10\"))\n"
+                        + "distributed by hash(k1) buckets 1\n" + 
"properties('replication_num' = '1'," +
+                        " 'data_sort.sort_type' = 'lexical');"));
+
+        // create z-order sort table, default col_num
+        ExceptionChecker.expectThrowsNoException(() -> createTable(
+                "create table test.zorder_tbl2\n" + "(k1 varchar(40), k2 int, 
k3 int)\n" + "duplicate key(k1, k2, k3)\n"
+                        + "partition by range(k2)\n" + "(partition p1 values 
less than(\"10\"))\n"
+                        + "distributed by hash(k1) buckets 1\n" + 
"properties('replication_num' = '1'," +
+                        " 'data_sort.sort_type' = 'zorder');"));
+
+        // create z-order sort table, define sort_col_num
+        ExceptionChecker.expectThrowsNoException(() -> createTable(
+                "create table test.zorder_tbl3\n" + "(k1 varchar(40), k2 int, 
k3 int)\n" + "duplicate key(k1, k2, k3)\n"
+                        + "partition by range(k2)\n" + "(partition p1 values 
less than(\"10\"))\n"
+                        + "distributed by hash(k1) buckets 1\n" + 
"properties('replication_num' = '1'," +
+                        " 'data_sort.sort_type' = 'zorder'," +
+                        " 'data_sort.col_num' = '2');"));
+        // create z-order sort table, only 1 sort column
+        ExceptionChecker
+                .expectThrowsWithMsg(AnalysisException.class, "z-order needs 2 
columns at least, 3 columns at most",
+                        () -> createTable("create table test.zorder_tbl4\n" + 
"(k1 varchar(40), k2 int, k3 int)\n" + "duplicate key(k1, k2, k3)\n"
+                                + "partition by range(k2)\n" + "(partition p1 
values less than(\"10\"))\n"
+                                + "distributed by hash(k1) buckets 1\n" + 
"properties('replication_num' = '1'," +
+                                " 'data_sort.sort_type' = 'zorder'," +
+                                " 'data_sort.col_num' = '1');"));
+        // create z-order sort table, sort column is empty
+        ExceptionChecker
+                .expectThrowsWithMsg(AnalysisException.class, "param 
data_sort.col_num error",
+                        () -> createTable("create table test.zorder_tbl4\n" + 
"(k1 varchar(40), k2 int, k3 int)\n" + "duplicate key(k1, k2, k3)\n"
+                                + "partition by range(k2)\n" + "(partition p1 
values less than(\"10\"))\n"
+                                + "distributed by hash(k1) buckets 1\n" + 
"properties('replication_num' = '1'," +
+                                " 'data_sort.sort_type' = 'zorder'," +
+                                " 'data_sort.col_num' = '');"));
+    }
 }
diff --git a/gensrc/proto/olap_file.proto b/gensrc/proto/olap_file.proto
index 622db02..96e4c2e 100644
--- a/gensrc/proto/olap_file.proto
+++ b/gensrc/proto/olap_file.proto
@@ -262,6 +262,11 @@ message ColumnPB {
     repeated string children_column_names = 18;
 }
 
+enum SortType {
+    LEXICAL = 0;
+    ZORDER  = 1;
+}
+
 message TabletSchemaPB {
     optional KeysType keys_type = 1;    // OLAPHeaderMessage.keys_type
     repeated ColumnPB column = 2;   // OLAPHeaderMessage.column
@@ -273,6 +278,8 @@ message TabletSchemaPB {
     optional bool is_in_memory = 8 [default=false];
     optional int32 delete_sign_idx = 9 [default = -1];
     optional int32 sequence_col_idx = 10 [default= -1];
+    optional SortType sort_type = 11;
+    optional int32 sort_col_num = 12;
 }
 
 enum TabletStatePB {
diff --git a/gensrc/thrift/AgentService.thrift 
b/gensrc/thrift/AgentService.thrift
index a18d6fd..3a98214 100644
--- a/gensrc/thrift/AgentService.thrift
+++ b/gensrc/thrift/AgentService.thrift
@@ -49,6 +49,8 @@ struct TTabletSchema {
     8: optional bool is_in_memory
     9: optional i32 delete_sign_idx = -1
     10: optional i32 sequence_col_idx = -1
+    11: optional Types.TSortType sort_type
+    12: optional i32 sort_col_num
 }
 
 // this enum stands for different storage format in src_backends
diff --git a/gensrc/thrift/Types.thrift b/gensrc/thrift/Types.thrift
index efa657a..506a97e 100644
--- a/gensrc/thrift/Types.thrift
+++ b/gensrc/thrift/Types.thrift
@@ -424,6 +424,11 @@ enum TMergeType {
   DELETE
 }
 
+enum TSortType {
+    LEXICAL,
+    ZORDER, 
+}
+
 // represent a user identity
 struct TUserIdentity {
     1: optional string username

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

Reply via email to