RocksDB

RocksDB là một công cụ lưu trữ với giao diện key/value, trong đó các khóa và giá trị là các luồng byte tùy ý. Nó được phát triển bởi Facebook bằng ngôn ngữ lập trình C++ dựa trên LevelDB.

Hướng dẫn cài đặt RocksDB trên RHEL/Centos 7

Hướng dẫn cài đặt CSDL RocksDB trên môi trường hệ điều hành Redhat/Centos 7

rocksdb.png

Yêu cầu:

Hướng dẫn cài đặt

-Cài gói gcc4.8:
yum install gcc48-c++
- Cài gói gflags:
cd /tmp/
git clone https://github.com/gflags/gflags.git
cd gflags
git checkout v2.0
./configure && make && sudo make install
- Thiết lập path:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
- Cài gói snappy:
sudo yum install snappy snappy-devel
- Cài gói zlib:
sudo yum install zlib zlib-devel
- Cài gói bzip2:
sudo yum install bzip2 bzip2-devel
- Cài gói lz4:
sudo yum install lz4-devel
- Cài gói ASAN (optional for debugging):
sudo yum install libasan
- Cài gói zstandard:
cd /tmp/
wget https://github.com/facebook/zstd/archive/v1.1.3.tar.gz
mv v1.1.3.tar.gz zstd-1.1.3.tar.gz
tar zxvf zstd-1.1.3.tar.gz
cd zstd-1.1.3
make && sudo make install
- Cài đặt RocksDB:
cd /tmp/
git clone https://github.com/facebook/rocksdb.git
cd rocksdb
make static_lib

Tham khảo

[1] https://github.com/facebook/rocksdb/blob/master/INSTALL.md

RocksDB - Open database

Để mở một database trong RocksDB, bạn có thể sử dụng command rocksdb::DB::Open. Dưới đây là một ví dụ về cách mở một database:

import org.rocksdb.*;

public class RocksDBExample {
    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {
            // Thực hiện các thao tác trên database ở đây
        } catch (RocksDBException e) {
            // Xử lý lỗi khi mở database không thành công
        }
    }
}

Trong ví dụ trên, options.setCreateIfMissing(true) được sử dụng để tạo một database mới nếu database chưa tồn tại.

Đây chỉ là một số command và ví dụ cơ bản về cách mở database trong RocksDB bằng Java. RocksDB cung cấp nhiều command và tùy chọn khác nhau để quản lý và truy xuất dữ liệu. 

Ví dụ C++:

#include "rocksdb/db.h"

rocksdb::DB* db;
rocksdb::Options options;
options.create_if_missing = true;
rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
if (!status.ok()) {
    // Xử lý lỗi khi mở database không thành công
} else {
    // Mở database thành công, bạn có thể thực hiện các thao tác khác trên database ở đây
}

RocksDB - Iterator

Trong RocksDB, iterator được sử dụng để duyệt qua toàn bộ RocksDB và truy xuất dữ liệu. Iterator cho phép bạn lấy giá trị của các key trong RocksDB theo thứ tự tuần tự.

Dưới đây là một ví dụ về cách sử dụng iterator trong RocksDB bằng Java:

import org.rocksdb.*;

public class RocksDBIteratorExample {
    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {
            try (final RocksIterator iterator = db.newIterator()) {
                for (iterator.seekToFirst(); iterator.isValid(); iterator.next()) {
                    byte[] key = iterator.key();
                    byte[] value = iterator.value();
                    System.out.println("Key: " + new String(key) + ", Value: " + new String(value));
                }
            }
        } catch (RocksDBException e) {
            // Xử lý lỗi khi mở database không thành công
        }
    }
}

Trong ví dụ trên, chúng ta sử dụng db.newIterator() để tạo một iterator mới cho RocksDB. Với mỗi phần tử trong RocksDB, chúng ta sử dụng iterator.key()iterator.value() để lấy giá trị của key và value tương ứng. Tiếp theo, chúng ta có thể thực hiện các thao tác xử lý với key và value như mong muốn.

Iterator trong RocksDB với C++

Đối với C++, RocksDB cung cấp một API tương tự để sử dụng iterator. Dưới đây là một ví dụ về cách sử dụng iterator trong RocksDB bằng C++:

#include "rocksdb/db.h"
#include "rocksdb/iterator.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::Iterator* it = db->NewIterator(rocksdb::ReadOptions());
        for (it->SeekToFirst(); it->Valid(); it->Next()) {
            rocksdb::Slice key = it->key();
            rocksdb::Slice value = it->value();
            std::cout << "Key: " << key.ToString() << ", Value: " << value.ToString() << std::endl;
        }
        delete it;
    } else {
        // Xử lý lỗi khi mở database không thành công
    }
    delete db;
    return 0;
}

Trong ví dụ trên, chúng ta sử dụng db->NewIterator(rocksdb::ReadOptions()) để tạo một iterator mới cho RocksDB. Với mỗi phần tử trong RocksDB, chúng ta sử dụng it->key()it->value() để lấy giá trị của key và value tương ứng. Tiếp theo, chúng ta có thể thực hiện các thao tác xử lý với key và value như mong muốn.

RocksDB - Put data

Trong RocksDB, Put được sử dụng để thêm một cặp key-value vào RocksDB. Dưới đây là các ví dụ về cách sử dụng Put trong RocksDB bằng Java và C++.

Java:

import org.rocksdb.*;

public class RocksDBPutExample {
    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {
            byte[] key = "myKey".getBytes();
            byte[] value = "myValue".getBytes();
            db.put(key, value);
        } catch (RocksDBException e) {
            // Xử lý lỗi khi thêm key-value không thành công
        }
    }
}

C++:

#include "rocksdb/db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::WriteOptions writeOptions;
        rocksdb::Slice key = "myKey";
        rocksdb::Slice value = "myValue";
        db->Put(writeOptions, key, value);
    } else {
        // Xử lý lỗi khi thêm key-value không thành công
    }
    delete db;
    return 0;
}

Trong cả hai ví dụ trên, chúng ta tạo một RocksDB database và sau đó sử dụng Put để thêm một cặp key-value vào database. Đường dẫn đến database và giá trị key-value có thể được tùy chỉnh theo nhu cầu của bạn.

Lưu ý rằng trong RocksDB, Put là một thao tác ghi đồng bộ, điều này có nghĩa là thao tác Put sẽ chờ cho đến khi dữ liệu thực sự được ghi vào ổ đĩa trước khi trả về. Điều này đảm bảo tính toàn vẹn của dữ liệu.

Đó là một số thông tin cơ bản về cách sử dụng Put trong RocksDB. Bạn có thể tìm hiểu thêm về các tùy chọn và tính năng khác của RocksDB trong tài liệu chi tiết của nó.

RocksDB - Get data

Trong RocksDB, Get được sử dụng để truy vấn giá trị của một key trong database. Dưới đây là các ví dụ về cách sử dụng Get trong RocksDB bằng Java và C++.

Java:

import org.rocksdb.*;

public class RocksDBGetExample {
    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {
            byte[] key = "myKey".getBytes();
            byte[] value = db.get(key);
            if (value != null) {
                System.out.println("Value: " + new String(value));
            } else {
                System.out.println("Key not found");
            }
        } catch (RocksDBException e) {
            // Xử lý lỗi khi truy vấn dữ liệu không thành công
        }
    }
}

C++:

#include "rocksdb/db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::ReadOptions readOptions;
        rocksdb::Slice key = "myKey";
        std::string value;
        rocksdb::Status get_status = db->Get(readOptions, key, &value);
        if (get_status.ok()) {
            std::cout << "Value: " << value << std::endl;
        } else if (get_status.IsNotFound()) {
            std::cout << "Key not found" << std::endl;
        } else {
            // Xử lý lỗi khi truy vấn dữ liệu không thành công
        }
    } else {
        // Xử lý lỗi khi mở database không thành công
    }
    delete db;
    return 0;
}

Trong cả hai ví dụ trên, chúng ta tạo một RocksDB database và sau đó sử dụng Get để truy vấn giá trị của một key trong database. Đường dẫn đến database và giá trị key có thể được tùy chỉnh theo nhu cầu của bạn.

Lưu ý rằng Get là một thao tác đồng bộ trong RocksDB. Điều này có nghĩa là thao tác Get sẽ chờ cho đến khi dữ liệu thực sự được đọc từ ổ đĩa trước khi trả về.

Đó là một số thông tin cơ bản về cách sử dụng Get trong RocksDB. Bạn có thể tìm hiểu thêm về các tùy chọn và tính năng khác của RocksDB trong tài liệu chi tiết của nó.

RocksDB - Delete data

Trong RocksDB, Delete được sử dụng để xóa một key và giá trị tương ứng khỏi RocksDB. Dưới đây là các ví dụ về cách sử dụng Delete trong RocksDB bằng Java và C++.

Java:

import org.rocksdb.*;

public class RocksDBDeleteExample {
    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {
            byte[] key = "myKey".getBytes();
            db.delete(key);
        } catch (RocksDBException e) {
            // Xử lý lỗi khi xóa key không thành công
        }
    }
}

C++:

#include "rocksdb/db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::WriteOptions writeOptions;
        rocksdb::Slice key = "myKey";
        rocksdb::Status delete_status = db->Delete(writeOptions, key);
        if (!delete_status.ok()) {
            // Xử lý lỗi khi xóa key không thành công
        }
    } else {
        // Xử lý lỗi khi mở database không thành công
    }
    delete db;
    return 0;
}

Trong cả hai ví dụ trên, chúng ta tạo một RocksDB database và sau đó sử dụng Delete để xóa một key và giá trị tương ứng khỏi database. Đường dẫn đến database và giá trị key có thể được tùy chỉnh theo nhu cầu của bạn.

Lưu ý rằng Delete là một thao tác ghi đồng bộ trong RocksDB. Điều này có nghĩa là thao tác Delete sẽ chờ cho đến khi dữ liệu xóa thực sự được ghi vào ổ đĩa trước khi trả về.

Đó là một số thông tin cơ bản về cách sử dụng Delete trong RocksDB. Bạn có thể tìm hiểu thêm về các tùy chọn và tính năng khác của RocksDB trong tài liệu chi tiết của nó.

RocksDB - Merge data

Trong RocksDB, Merge được sử dụng để thực hiện phép gộp (merge) các giá trị tương ứng với một key trong RocksDB. Phép gộp này sẽ cộng giá trị mới vào giá trị hiện có của key đó, hoặc tạo giá trị mới nếu key chưa tồn tại trong RocksDB.

Dưới đây là một ví dụ về cách sử dụng Merge trong RocksDB bằng C++:

#include "rocksdb/db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::WriteOptions writeOptions;
        rocksdb::Slice key = "myKey";
        rocksdb::Slice value = "myValue";
        db->Merge(writeOptions, key, value);
    } else {
        // Xử lý lỗi khi thực hiện merge không thành công
    }
    delete db;
    return 0;
}

Trong ví dụ trên, chúng ta tạo một RocksDB database và sau đó sử dụng Merge để thực hiện phép gộp các giá trị tương ứng với key "myKey". Nếu key đã tồn tại trong RocksDB, giá trị mới sẽ được cộng vào giá trị hiện có của key đó. Nếu key chưa tồn tại, RocksDB sẽ tạo một giá trị mới với giá trị được gán là "myValue".

Lưu ý rằng Merge là một thao tác ghi đồng bộ trong RocksDB, điều này có nghĩa là thao tác Merge sẽ chờ cho đến khi dữ liệu thực sự được ghi vào ổ đĩa trước khi trả về.

Đó là một số thông tin cơ bản về cách sử dụng Merge trong RocksDB. Bạn có thể tìm hiểu thêm về các tùy chọn và tính năng khác của RocksDB trong tài liệu chi tiết của nó.

RocksDB - Write batch

WriteBatch trong RocksDB được sử dụng để thực hiện nhiều thao tác ghi (put, delete, merge) cùng một lúc, giúp tăng hiệu suất ghi dữ liệu. Điều này có thể đặc biệt hữu ích khi bạn muốn thực hiện nhiều thao tác ghi một cách liên tục mà không muốn mỗi thao tác phải chờ cho đến khi dữ liệu được ghi vào ổ đĩa trước khi thực hiện thao tác tiếp theo.

Dưới đây là một ví dụ về cách sử dụng WriteBatch trong RocksDB bằng C++:

#include "rocksdb/db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::WriteBatch batch;
        batch.Put("key1", "value1");
        batch.Delete("key2");
        batch.Merge("key3", "value3");
        rocksdb::WriteOptions writeOptions;
        rocksdb::Status write_status = db->Write(writeOptions, &batch);
        if (write_status.ok()) {
            // Thực hiện thành công
        } else {
            // Xử lý lỗi khi thực hiện ghi không thành công
        }
    } else {
        // Xử lý lỗi khi mở database không thành công
    }
    delete db;
    return 0;
}

Java:

import org.rocksdb.*;

public class RocksDBWriteBatchExample {
    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {
            try (final WriteBatch batch = new WriteBatch()) {
                byte[] key1 = "key1".getBytes();
                byte[] value1 = "value1".getBytes();
                batch.put(key1, value1);

                byte[] key2 = "key2".getBytes();
                batch.delete(key2);

                byte[] key3 = "key3".getBytes();
                byte[] value3 = "value3".getBytes();
                batch.merge(key3, value3);

                WriteOptions writeOptions = new WriteOptions();
                db.write(writeOptions, batch);
            } catch (RocksDBException e) {
                // Xử lý lỗi khi thực hiện ghi không thành công
            }
        } catch (RocksDBException e) {
            // Xử lý lỗi khi mở database không thành công
        }
    }
}

Trong ví dụ trên, chúng ta tạo một RocksDB database và sau đó sử dụng WriteBatch để thực hiện các thao tác ghi (put, delete, merge) trên các key tương ứng. Sau khi thêm các thao tác ghi vào WriteBatch, chúng ta sử dụng phương thức Write của RocksDB để thực hiện ghi tất cả các thao tác trong WriteBatch cùng một lúc.

Lưu ý rằng WriteBatch là một thao tác ghi đồng bộ trong RocksDB. Điều này có nghĩa là thao tác Write sẽ chờ cho đến khi tất cả các thao tác ghi trong WriteBatch được ghi vào ổ đĩa trước khi trả về.

Đó là một số thông tin cơ bản về cách sử dụng WriteBatch trong RocksDB. Bạn có thể tìm hiểu thêm về các tùy chọn và tính năng khác của RocksDB trong tài liệu chi tiết của nó.

RocksDB - Rate Limiter

Rate Limiter trong RocksDB được sử dụng để giới hạn tốc độ đọc/ghi dữ liệu vào RocksDB. Điều này có thể hữu ích trong trường hợp đọc/ghi dữ liệu đang gây ảnh hưởng đến hoạt động của hệ thống khác.

Ví dụ sau đây minh họa cách sử dụng Rate Limiter trong RocksDB:

rocksdb::Options options;
options.rate_limiter = rocksdb::NewGenericRateLimiter(1000000, 10000, 10);
rocksdb::DB* db;
rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/db", &db);
if (status.ok()) {
  // read/write data to db
}

Trong ví dụ này, chúng tôi tạo một Rate Limiter với tốc độ tối đa là 1MB/giây, tối đa 10.000 yêu cầu và giới hạn là 10MB. Sau đó, chúng tôi sử dụng Rate Limiter này khi mở RocksDB bằng cách đặt nó trong tùy chọn của RocksDB.

Một khi Rate Limiter đã được đặt, RocksDB sẽ tự động điều chỉnh tốc độ đọc/ghi dữ liệu vào RocksDB để đảm bảo không vượt quá giới hạn được xác định bởi Rate Limiter.

Đó là ví dụ cơ bản về việc sử dụng Rate Limiter trong RocksDB. Việc sử dụng Rate Limiter có thể giúp tối ưu hoá hiệu suất của RocksDB trong một số trường hợp cụ thể.

Ngoài ra, bạn cũng có thể sử dụng Rate Limiter trong RocksDB khi sử dụng Java bằng cách sử dụng các API của RocksJava. Dưới đây là một ví dụ về cách sử dụng Rate Limiter trong RocksJava:

Options options = new Options();
options.setCreateIfMissing(true);
RateLimiter rateLimiter = new GenericRateLimiter(1000000, 10000, 10);
options.setRateLimiter(rateLimiter);
try (RocksDB db = RocksDB.open(options, "/path/to/db")) {
  // read/write data to db
}

Trong ví dụ này, chúng tôi tạo một Rate Limiter với tốc độ tối đa là 1MB/giây, tối đa 10.000 yêu cầu và giới hạn là 10MB. Sau đó, chúng tôi sử dụng Rate Limiter này khi mở RocksDB bằng cách đặt nó trong tùy chọn của RocksDB.

Một khi Rate Limiter đã được đặt, RocksDB sẽ tự động điều chỉnh tốc độ đọc/ghi dữ liệu vào RocksDB để đảm bảo không vượt quá giới hạn được xác định bởi Rate Limiter.

Đó là cách sử dụng Rate Limiter trong RocksJava. Việc sử dụng Rate Limiter có thể giúp tối ưu hoá hiệu suất của RocksDB trong một số trường hợp cụ thể.

RocksDB - Prefix Seek

Prefix Seek trong RocksDB được sử dụng để tìm kiếm các key bắt đầu bằng một tiền tố cụ thể trong database. Phương thức Prefix Seek trả về iterator trỏ đến vị trí đầu tiên của key có tiền tố được chỉ định.

Dưới đây là một ví dụ về cách sử dụng Prefix Seek trong RocksDB bằng C++:

#include "rocksdb/db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::ReadOptions readOptions;
        rocksdb::Iterator* iterator = db->NewIterator(readOptions);
        rocksdb::Slice prefix = "myPrefix";
        iterator->Seek(prefix);
        while (iterator->Valid() && iterator->key().starts_with(prefix)) {
            // Xử lý key tương ứng
            iterator->Next();
        }
        delete iterator;
    } else {
        // Xử lý lỗi khi mở database không thành công
    }
    delete db;
    return 0;
}

Trong ví dụ trên, chúng ta tạo một RocksDB database và sử dụng Prefix Seek để tìm kiếm các key bắt đầu bằng tiền tố "myPrefix". Iterator trả về sẽ trỏ đến vị trí đầu tiên của key tương ứng. Tiếp theo, chúng ta sử dụng vòng lặp để duyệt qua các key có tiền tố "myPrefix". Khi iterator không còn hợp lệ hoặc key không còn bắt đầu bằng tiền tố "myPrefix", vòng lặp sẽ kết thúc.

Lưu ý rằng Prefix Seek không trả về các key theo thứ tự sắp xếp. Nếu bạn muốn lấy các key theo thứ tự sắp xếp, bạn cần thực hiện sắp xếp trong vòng lặp hoặc sử dụng các phương thức khác như Prefix Enumerate trong RocksDB.

Đó là một số thông tin cơ bản về cách sử dụng Prefix Seek trong RocksDB bằng C++. Bạn có thể tìm hiểu thêm về các tùy chọn và tính năng khác của RocksDB trong tài liệu chi tiết của nó.

RocksDB - Compaction Filter

Compaction Filter trong RocksDB là một cơ chế cho phép bạn thực hiện các thay đổi tùy chỉnh trên dữ liệu khi RocksDB thực hiện quá trình Compaction. Quá trình Compaction là quá trình tối ưu hóa dữ liệu trong RocksDB bằng cách ghép các file SST thành file mới để tiết kiệm không gian lưu trữ và tăng hiệu suất truy vấn.

Với Compaction Filter, bạn có thể thay đổi giá trị của key hoặc loại bỏ key khỏi RocksDB trong quá trình Compaction. Điều này rất hữu ích khi bạn muốn áp dụng các quy tắc tùy chỉnh cho dữ liệu trong quá trình Compaction.

Dưới đây là một ví dụ về việc sử dụng Compaction Filter trong RocksDB bằng Java:

import org.rocksdb.*;

public class RocksDBCompactionFilterExample {

    public static class MyCompactionFilter extends AbstractCompactionFilter {
        @Override
        public boolean filter(
                int level, byte[] key, byte[] existingValue, byte[] value) {
            // Kiểm tra điều kiện tùy chỉnh và quyết định có loại bỏ key hay không
            // Trả về true nếu muốn loại bỏ key, ngược lại trả về false
            return false;
        }
    }

    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {
            final ColumnFamilyHandle cfHandle = db.getDefaultColumnFamily();
            db.addCompactionFilter(cfHandle, new MyCompactionFilter());
        } catch (RocksDBException e) {
            // Xử lý lỗi khi thêm Compaction Filter không thành công
        }
    }
}

C++:

#include "rocksdb/db.h"

class MyCompactionFilter : public rocksdb::CompactionFilter {
public:
    bool Filter(int level,
                const rocksdb::Slice& key,
                const rocksdb::Slice& existing_value,
                std::string* new_value,
                bool* value_changed) const override {
        // Kiểm tra điều kiện tùy chỉnh và quyết định có loại bỏ key hay không
        // Trả về true nếu muốn loại bỏ key, ngược lại tr

Trong ví dụ trên, chúng ta tạo một class MyCompactionFilter kế thừa từ AbstractCompactionFilter và ghi đè phương thức filter(). Trong phương thức filter(), chúng ta có thể kiểm tra các điều kiện tùy chỉnh và quyết định xem có loại bỏ key hay không. Nếu muốn loại bỏ key, chúng ta trả về true, ngược lại trả về false.

Sau đó, chúng ta thêm Compaction Filter vào RocksDB bằng cách sử dụng phương thức addCompactionFilter(). Trong trường hợp này, chúng ta sử dụng getDefaultColumnFamily() để lấy handle của column family mặc định trong RocksDB.

RocksDB - Column Families

Column Families trong RocksDB là một tính năng cho phép bạn tổ chức và quản lý dữ liệu theo các nhóm được đặt tên. Mỗi Column Family có thể có các tùy chọn và cấu hình riêng, cho phép bạn tuỳ chỉnh và tối ưu hóa cách RocksDB lưu trữ và truy xuất dữ liệu.

Dưới đây là một ví dụ về cách sử dụng Column Families trong RocksDB bằng Java:

import org.rocksdb.*;

public class RocksDBColumnFamiliesExample {
    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {
            try (final ColumnFamilyHandle cfHandle = db.createColumnFamily(new ColumnFamilyDescriptor("myColumnFamily".getBytes()))) {
                byte[] key = "myKey".getBytes();
                byte[] value = "myValue".getBytes();

                db.put(cfHandle, key, value);

                byte[] retrievedValue = db.get(cfHandle, key);
                System.out.println("Retrieved value: " + new String(retrievedValue));
            } catch (RocksDBException e) {
                // Xử lý lỗi khi tạo Column Family không thành công
            }
        } catch (RocksDBException e) {
            // Xử lý lỗi khi mở database không thành công
        }
    }
}

Trong ví dụ trên, chúng ta tạo một RocksDB database và sử dụng createColumnFamily() để tạo một Column Family mới với tên "myColumnFamily". Sau đó, chúng ta sử dụng put() để lưu trữ một cặp key-value trong Column Family. Cuối cùng, chúng ta sử dụng get() để lấy giá trị tương ứng với một key từ Column Family.

Lưu ý rằng chúng ta sử dụng try-with-resources để đảm bảo rằng các resources của RocksDB được đóng một cách chính xác sau khi sử dụng xong.

Đó là một ví dụ về cách sử dụng Column Families trong RocksDB bằng Java.

C++:

#include "rocksdb/db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::ColumnFamilyHandle* cfHandle;
        rocksdb::ColumnFamilyOptions cfOptions;
        rocksdb::Status cfStatus = db->CreateColumnFamily(cfOptions, "myColumnFamily", &cfHandle);
        if (cfStatus.ok()) {
            rocksdb::WriteOptions writeOptions;
            rocksdb::Slice key = "myKey";
            rocksdb::Slice value = "myValue";
            db->Put(writeOptions, cfHandle, key, value);

            std::string retrievedValue;
            rocksdb::ReadOptions readOptions;
            rocksdb::Status retrieveStatus = db->Get(readOptions, cfHandle, key, &retrievedValue);
            if (retrieveStatus.ok()) {
                std::cout << "Retrieved value: " << retrievedValue << std::endl;
            } else {
                // Xử lý lỗi khi lấy giá trị không thành công
            }
        } else

RocksDB - Low Priority Write

Low Priority Write là một tính năng trong RocksDB cho phép bạn ghi dữ liệu vào RocksDB với mức độ ưu tiên thấp hơn so với các ghi viết thông thường. Khi sử dụng Low Priority Write, RocksDB sẽ ưu tiên ghi các ghi chép (write-ahead log) và các ghi viết quan trọng hơn trước, giúp tăng hiệu suất ghi dữ liệu và giảm thời gian đáp ứng của RocksDB.

Dưới đây là một ví dụ về việc sử dụng Low Priority Write trong RocksDB bằng Java:

import org.rocksdb.*;

public class RocksDBLowPriorityWriteExample {

    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {
            final WriteOptions writeOptions = new WriteOptions().setLowPriority(true);

            byte[] key = "myKey".getBytes();
            byte[] value = "myValue".getBytes();

            db.put(writeOptions, key, value);
        } catch (RocksDBException e) {
            // Xử lý lỗi khi ghi dữ liệu không thành công
        }
    }
}

Trong ví dụ trên, chúng ta sử dụng setLowPriority(true) để đặt mức độ ưu tiên của ghi dữ liệu là thấp (Low Priority). Sau đó, chúng ta sử dụng put() để ghi một cặp key-value vào RocksDB với mức độ ưu tiên thấp.

Đó là một ví dụ về việc sử dụng Low Priority Write trong RocksDB bằng Java.

C++:

#include "rocksdb/db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::WriteOptions writeOptions;
        writeOptions.low_priority = true;
        rocksdb::Slice key = "myKey";
        rocksdb::Slice value = "myValue";
        rocksdb::Status writeStatus = db->Put(writeOptions, key, value);
        if (!writeStatus.ok()) {
            // Xử lý lỗi khi ghi dữ liệu không thành công
        }
    } else {
        // Xử lý lỗi khi mở database không thành công
    }
    delete db;
    return 0;
}

Trong ví dụ trên, chúng ta sử dụng writeOptions.low_priority = true để đặt mức độ ưu tiên của ghi dữ liệu là thấp (Low Priority). Sau đó, chúng ta sử dụng Put() để ghi một cặp key-value vào RocksDB với mức độ ưu tiên thấp.

Đó là một ví dụ về việc sử dụng Low Priority Write trong RocksDB bằng C++.

RocksDB - Time To Live

TTL (Time-to-Live) là một tính năng trong RocksDB cho phép bạn đặt thời gian sống cho một cặp key-value. Khi giá trị TTL hết hạn, RocksDB tự động xóa key-value tương ứng khỏi cơ sở dữ liệu.

Dưới đây là một ví dụ về việc sử dụng TTL trong RocksDB bằng Java:

import org.rocksdb.*;

public class RocksDBTTLExample {

    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {
            final ColumnFamilyHandle cfHandle = db.getDefaultColumnFamily();

            byte[] key = "myKey".getBytes();
            byte[] value = "myValue".getBytes();
            int ttl = 3600; // TTL in seconds

            db.put(cfHandle, key, value, ttl);
        } catch (RocksDBException e) {
            // Xử lý lỗi khi ghi dữ liệu không thành công
        }
    }
}

Trong ví dụ trên, chúng ta sử dụng put() để ghi một cặp key-value vào RocksDB với một giá trị TTL (Time-to-Live) được đặt là 3600 giây (1 giờ). Khi giá trị TTL hết hạn, RocksDB tự động xóa key-value tương ứng.

Đối với việc sử dụng TTL trong RocksDB bằng C++, dưới đây là một ví dụ:

#include "rocksdb/db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::WriteOptions writeOptions;
        rocksdb::Slice key = "myKey";
        rocksdb::Slice value = "myValue";
        int ttl = 3600; // TTL in seconds
        rocksdb::Status writeStatus = db->Put(writeOptions, key, value, rocksdb::WriteTTL(ttl));
        if (!writeStatus.ok()) {
            // Xử lý lỗi khi ghi dữ liệu không thành công
        }
    } else {
        // Xử lý lỗi khi mở cơ sở dữ liệu không thành công
    }
    delete db;
    return 0;
}

Trong ví dụ trên, chúng ta sử dụng Put() để ghi một cặp key-value vào RocksDB với một giá trị TTL (Time-to-Live) được đặt là 3600 giây (1 giờ). Khi giá trị TTL hết hạn, RocksDB tự động xóa key-value tương ứng.

Đó là một ví dụ về việc sử dụng TTL trong RocksDB bằng Java và C++.

RocksDB - Transaction

Transactions (giao dịch) là một tính năng quan trọng trong RocksDB cho phép bạn thực hiện các thao tác đọc và ghi dữ liệu theo cách an toàn và nhất quán. Khi bạn thực hiện các thao tác trong một giao dịch, RocksDB đảm bảo rằng các thay đổi chỉ được áp dụng hoàn toàn hoặc không áp dụng.

Dưới đây là một ví dụ về việc sử dụng Transactions trong RocksDB bằng Java:

import org.rocksdb.*;

public class RocksDBTransactionsExample {

    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {
            try (final TransactionDB transactionDb = TransactionDB.open(options, "/path/to/transaction_database")) {
                // Bắt đầu một giao dịch
                try (final Transaction transaction = transactionDb.beginTransaction(new WriteOptions())) {
                    byte[] key1 = "key1".getBytes();
                    byte[] key2 = "key2".getBytes();
                    byte[] value1 = "value1".getBytes();
                    byte[] value2 = "value2".getBytes();

                    // Thực hiện các thao tác ghi dữ liệu trong giao dịch
                    db.put(transaction, key1, value1);
                    db.put(transaction, key2, value2);

                    // Xác nhận giao dịch thành công
                    transaction.commit();
                } catch (RocksDBException e) {
                    // Xử lý lỗi khi giao dịch không thành công
                }
            } catch (RocksDBException e) {
                // Xử lý lỗi khi mở transaction database không thành công
            }
        } catch (RocksDBException e) {
            // Xử lý lỗi khi mở database không thành công
        }
    }
}

Trong ví dụ trên, chúng ta sử dụng TransactionDB.open() để mở một transaction database. Sau đó, chúng ta bắt đầu một giao dịch bằng cách sử dụng beginTransaction(). Trong giao dịch, chúng ta thực hiện các thao tác ghi dữ liệu bằng put(). Cuối cùng, chúng ta xác nhận giao dịch thành công bằng commit(). Nếu có lỗi xảy ra, chúng ta có thể sử dụng rollback() để hủy bỏ giao dịch.

Đây là một ví dụ về việc sử dụng Transactions trong RocksDB bằng Java.

C++:

#include "rocksdb/db.h"
#include "rocksdb/utilities/transaction.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::TransactionDBOptions txdbOptions;
        txdbOptions.create_if_missing = true;
        rocksdb::TransactionDB* transactionDb;
        rocksdb::TransactionDB::Open(txdbOptions, "/path/to/transaction_database", &transactionDb);

        // Bắt đầu một giao dịch
        rocksdb::WriteOptions writeOptions;
        rocksdb

RocksDB - Snapshot data

Snapshot là một tính năng trong RocksDB cho phép bạn tạo bản sao dữ liệu hiện tại để đọc theo một thời điểm cụ thể. Khi bạn tạo một snapshot, RocksDB đảm bảo rằng dữ liệu trong snapshot không bị ảnh hưởng bởi các thay đổi sau đó. Điều này rất hữu ích khi bạn muốn đọc dữ liệu trong một trạng thái nhất định và tránh bị ảnh hưởng bởi các giao dịch hoặc thay đổi dữ liệu khác.

Dưới đây là một ví dụ về việc sử dụng Snapshot trong RocksDB bằng Java:

import org.rocksdb.*;

public class RocksDBSnapshotExample {

    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {
            try (final Snapshot snapshot = db.getSnapshot()) {
                // Đọc dữ liệu từ snapshot
                try (final ReadOptions readOptions = new ReadOptions().setSnapshot(snapshot)) {
                    byte[] key = "myKey".getBytes();
                    byte[] value = db.get(readOptions, key);
                    if (value != null) {
                        System.out.println("Value for key 'myKey': " + new String(value));
                    } else {
                        System.out.println("Value not found for key 'myKey'");
                    }
                }
            }
        } catch (RocksDBException e) {
            // Xử lý lỗi khi mở database không thành công
        }
    }
}

Trong ví dụ trên, chúng ta sử dụng getSnapshot() để tạo một snapshot của RocksDB. Sau đó, chúng ta sử dụng setSnapshot() để thiết lập snapshot cho các thao tác đọc dữ liệu. Bằng cách này, RocksDB đảm bảo rằng các thay đổi sau snapshot không ảnh hưởng đến dữ liệu được đọc từ snapshot. Trong ví dụ, chúng ta đọc giá trị của key "myKey" từ snapshot.

Đó là một ví dụ về việc sử dụng Snapshot trong RocksDB bằng Java.

C++:

#include "rocksdb/db.h"
#include "rocksdb/utilities/transaction_db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::TransactionDBOptions txdbOptions;
        txdbOptions.create_if_missing = true;
        rocksdb::TransactionDB* transactionDb;
        rocksdb::TransactionDB::Open(txdbOptions, "/path/to/transaction_database", &transactionDb);

        // Bắt đầu một giao dịch
        rocksdb::WriteOptions writeOptions;
        rocksdb::TransactionOptions txnOptions;
        rocksdb::Transaction* txn = transactionDb->BeginTransaction(writeOptions, txnOptions);

        // Tạo một snapshot
        const rocksdb::Snapshot* snapshot = txn->GetSnapshot();

        // Đọc dữ liệu từ snapshot
        rocksdb::ReadOptions readOptions;
        readOptions.snapshot = snapshot;
        std::string value;
        txn->Get(readOptions, "myKey",

RocksDB - Delete Range

Trong RocksDB, DeleteRange() là một hàm dùng để xóa một phạm vi dữ liệu trong cơ sở dữ liệu RocksDB. Nó cho phép bạn xóa tất cả các key-value có key nằm trong một khoảng key được chỉ định.

Dưới đây là một ví dụ về việc sử dụng DeleteRange() trong RocksDB bằng Java:

import org.rocksdb.*;

public class RocksDBDeleteRangeExample {

    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {

            byte[] startKey = "startKey".getBytes();
            byte[] endKey = "endKey".getBytes();

            WriteOptions writeOptions = new WriteOptions();
            db.deleteRange(writeOptions, startKey, endKey);
        } catch (RocksDBException e) {
            // Xử lý lỗi khi xóa dữ liệu không thành công
        }
    }
}

Trong ví dụ trên, chúng ta sử dụng deleteRange() để xóa tất cả các key-value có key trong khoảng từ "startKey" đến "endKey" trong RocksDB.

Đây là một ví dụ về việc sử dụng DeleteRange() trong RocksDB bằng Java.

C++:

#include "rocksdb/db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        rocksdb::WriteOptions writeOptions;
        rocksdb::Slice startKey = "startKey";
        rocksdb::Slice endKey = "endKey";
        rocksdb::Status deleteStatus = db->DeleteRange(writeOptions, &startKey, &endKey);
        if (!deleteStatus.ok()) {
            // Xử lý lỗi khi xóa dữ liệu không thành công
        }
    } else {
        // Xử lý lỗi khi mở cơ sở dữ liệu không thành công
    }
    delete db;
    return 0;
}

Trong ví dụ trên, chúng ta sử dụng DeleteRange() để xóa tất cả các key-value có key trong khoảng từ "startKey" đến "endKey" trong RocksDB.

Đó là một ví dụ về việc sử dụng DeleteRange() trong RocksDB bằng Java và C++.

RocksDB - Atomic Flush

Atomic Flush là một tính năng trong RocksDB cho phép bạn đảm bảo rằng tất cả các thay đổi dữ liệu đã được ghi vào bộ nhớ đệm sẽ được ghi xuống đĩa cùng một lúc. Điều này đảm bảo tính nhất quán của dữ liệu và tránh mất mát dữ liệu do quá trình ghi bị hỏng.

Trong RocksDB, bạn có thể kích hoạt Atomic Flush bằng cách sử dụng SetAtomicFlush() trong Options.

Dưới đây là một ví dụ về việc sử dụng Atomic Flush trong RocksDB bằng Java:

import org.rocksdb.*;

public class RocksDBAtomicFlushExample {

    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true)) {
            options.setAtomicFlush(true);
            try (final RocksDB db = RocksDB.open(options, "/path/to/database")) {
                // Thực hiện các thao tác ghi dữ liệu
                byte[] key = "myKey".getBytes();
                byte[] value = "myValue".getBytes();
                db.put(key, value);
                // Kích hoạt Atomic Flush
                db.flush(new FlushOptions().setWaitForFlush(true));
            } catch (RocksDBException e) {
                // Xử lý lỗi khi mở database không thành công
            }
        }
    }
}

Trong ví dụ trên, chúng ta sử dụng setAtomicFlush(true) để kích hoạt Atomic Flush trong RocksDB. Sau đó, chúng ta thực hiện các thao tác ghi dữ liệu và sử dụng flush() để ghi xuống đĩa. Bằng cách sử dụng setWaitForFlush(true), RocksDB đảm bảo rằng tất cả các thay đổi đã được ghi xuống đĩa trước khi tiếp tục thực thi chương trình.

Đây là một ví dụ về việc sử dụng Atomic Flush trong RocksDB bằng Java.

C++:

#include "rocksdb/db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    options.atomic_flush = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        // Thực hiện các thao tác ghi dữ liệu
        rocksdb::WriteOptions writeOptions;
        rocksdb::Slice key = "myKey";
        rocksdb::Slice value = "myValue";
        rocksdb::Status putStatus = db->Put(writeOptions, key, value);
        if (putStatus.ok()) {
            // Kích hoạt Atomic Flush
            rocksdb::FlushOptions flushOptions;
            flushOptions.wait = true;
            rocksdb::Status flushStatus = db->Flush(flushOptions);
            if (!flushStatus.ok()) {
                // Xử lý lỗi khi ghi xuống đĩa không thành công
            }
        } else {
            // Xử lý lỗi khi ghi dữ liệu không thành công
        }
    } else {
        // Xử lý lỗi khi mở cơ sở dữ liệu không thành công
    }
    delete db;
    return

RocksDB - Read only và Secondary instances

Các trường hợp sử dụng Read-Only Instances

Read-Only Instances trong RocksDB được sử dụng để đảm bảo tính nhất quán và hiệu suất đọc trong môi trường có nhiều tiến trình đọc. Dưới đây là một số trường hợp sử dụng Read-Only Instances:

Cách sử dụng Secondary Instances

Secondary Instances trong RocksDB được sử dụng để tạo các bản sao dự phòng của cơ sở dữ liệu và đồng bộ hóa dữ liệu với Primary Instance. Dưới đây là một số bước để sử dụng Secondary Instances:

  1. Cấu hình Primary Instance: Đầu tiên, bạn cần cấu hình và khởi tạo Primary Instance theo các bước thông thường.
  2. Cấu hình Secondary Instance: Tiếp theo, bạn cần cấu hình và khởi tạo Secondary Instance bằng cách sử dụng các tùy chọn và thiết lập đồng bộ hóa dữ liệu với Primary Instance. Đảm bảo rằng bạn đã thiết lập đúng địa chỉ và cổng của Primary Instance để Secondary Instance có thể kết nối và đồng bộ dữ liệu.
  3. Khởi động Secondary Instance: Sau khi cấu hình, bạn có thể khởi động Secondary Instance để nó bắt đầu sao chép dữ liệu từ Primary Instance và duy trì đồng bộ hóa dữ liệu.
  4. Đọc dữ liệu từ Secondary Instances: Bây giờ, bạn có thể thực hiện các thao tác đọc dữ liệu từ Secondary Instances một cách an toàn và tin cậy.

Lưu ý rằng Secondary Instances chỉ hỗ trợ đọc dữ liệu và không hỗ trợ ghi. Ghi dữ liệu vẫn phải được thực hiện qua Primary Instance.

Đó là một số chi tiết về Read-Only và Secondary Instances trong RocksDB.

RocksDB - Wide Columns

Wide Columns là một tính năng trong RocksDB cho phép lưu trữ và truy xuất dữ liệu trong các cột rộng. Điều này cho phép bạn lưu trữ các giá trị có cấu trúc phức tạp, như mảng, đối tượng hoặc bất kỳ kiểu dữ liệu tùy chỉnh nào, dưới dạng một cột duy nhất.

Để sử dụng Wide Columns trong RocksDB, bạn cần sử dụng các hàm API để đọc và ghi dữ liệu cho các cột rộng. Dưới đây là một ví dụ về việc sử dụng Wide Columns trong RocksDB bằng Java:

import org.rocksdb.*;

public class RocksDBWideColumnsExample {

    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {

            // Ghi dữ liệu vào Wide Columns
            byte[] columnFamilyName = "myColumnFamily".getBytes();
            ColumnFamilyHandle columnFamilyHandle = db.createColumnFamily(new ColumnFamilyDescriptor(columnFamilyName));
            byte[] key = "myKey".getBytes();
            byte[] value = "myValue".getBytes();
            db.put(columnFamilyHandle, key, value);

            // Đọc dữ liệu từ Wide Columns
            byte[] readValue = db.get(columnFamilyHandle, key);
            System.out.println("Value: " + new String(readValue));
        } catch (RocksDBException e) {
            // Xử lý lỗi khi thao tác với cơ sở dữ liệu không thành công
        }
    }
}

Trong ví dụ trên, chúng ta sử dụng createColumnFamily() để tạo một Column Family mới cho Wide Columns. Sau đó, chúng ta sử dụng put() để ghi dữ liệu vào cột rộng và get() để đọc dữ liệu từ cột rộng.

Dưới đây là một ví dụ về việc sử dụng Wide Columns trong RocksDB bằng C++:

#include "rocksdb/db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        // Ghi dữ liệu vào Wide Columns
        rocksdb::ColumnFamilyHandle* columnFamilyHandle;
        rocksdb::ColumnFamilyOptions cfOptions;
        rocksdb::Status createCfStatus = db->CreateColumnFamily(cfOptions, "myColumnFamily", &columnFamilyHandle);
        if (createCfStatus.ok()) {
            rocksdb::WriteOptions writeOptions;
            rocksdb::Slice key = "myKey";
            rocksdb::Slice value = "myValue";
            rocksdb::Status putStatus = db->Put(writeOptions, columnFamilyHandle, key, value);
            if (!putStatus.ok()) {
                // Xử lý lỗi khi ghi dữ liệu không thành công
            }
        } else {
            // Xử lý lỗi khi tạo Column Family không thành công
        }

        // Đọc dữ liệu từ Wide Columns
        rocksdb::ReadOptions readOptions;
        rocksdb::PinnableSlice readValue;
        rocksdb::Status getStatus = db->Get(readOptions, columnFamilyHandle, key, &readValue

RocksDB - BlobDB

BlobDB là một mô-đun bổ sung cho RocksDB, được thiết kế đặc biệt để lưu trữ và quản lý dữ liệu lớn không cấu trúc như văn bản, hình ảnh, âm thanh, video và các đối tượng nhị phân khác. Nó cung cấp khả năng lưu trữ hiệu quả dữ liệu lớn và quản lý metadata liên quan đến dữ liệu.

BlobDB sử dụng một cấu trúc dữ liệu gọi là "blob" để lưu trữ dữ liệu không cấu trúc. Mỗi blob là một đơn vị lưu trữ độc lập, có thể chứa một hoặc nhiều đối tượng nhị phân. BlobDB sử dụng RocksDB để lưu trữ metadata của các blob, bao gồm thông tin như kích thước, thời gian tạo và các thuộc tính tùy chỉnh khác.

BlobDB cung cấp các API để thêm, truy xuất và xóa blob, cho phép bạn làm việc với dữ liệu không cấu trúc một cách dễ dàng và hiệu quả. Dưới đây là một ví dụ về việc sử dụng BlobDB trong RocksDB bằng Java:

import org.rocksdb.*;

public class RocksDBBlobDBExample {

    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {

            // Khởi tạo BlobDB
            try (final BlobDB blobDB = BlobDB.open(db)) {
                // Thêm dữ liệu vào BlobDB
                byte[] blobId = blobDB.put("Hello, BlobDB!".getBytes());
                System.out.println("Blob ID: " + new String(blobId));

                // Truy xuất dữ liệu từ BlobDB
                byte[] data = blobDB.get(blobId);
                System.out.println("Data: " + new String(data));

                // Xóa dữ liệu từ BlobDB
                blobDB.delete(blobId);
            } catch (RocksDBException e) {
                // Xử lý lỗi khi thao tác với BlobDB không thành công
            }
        } catch (RocksDBException e) {
            // Xử lý lỗi khi thao tác với cơ sở dữ liệu RocksDB không thành công
        }
    }
}

Trong ví dụ trên, chúng ta sử dụng BlobDB.open() để khởi tạo một phiên làm việc với BlobDB. Sau đó, chúng ta có thể sử dụng put() để thêm dữ liệu vào BlobDB, get() để truy xuất dữ liệu từ BlobDB và delete() để xóa dữ liệu từ BlobDB.

Đây là một ví dụ về việc sử dụng BlobDB trong RocksDB bằng C++:

#include "rocksdb/db.h"
#include "rocksdb/blob_db.h"

int main() {
    rocksdb::DB* db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::Status status = rocksdb::DB::Open(options, "/path/to/database", &db);
    if (status.ok()) {
        // Khởi tạo BlobDB
        rocksdb::BlobDB* blobDB;
        rocksdb::BlobDBOptions blobDBOptions;
        rocksdb::Status blobDBStatus = rocksdb::BlobDB::Open(blobDBOptions, db, &blobDB);
        if (blobDBStatus.ok()) {
            // Thêm dữ liệu vào BlobDB
            rocksdb::BlobDB::PutOptions putOptions;
            rocksdb::Slice data = "Hello, BlobDB!";
            rocksdb::BlobIndex blobIndex;
            rocksdb::Status putStatus = blobDB->Put(putOptions, data, &blobIndex);
            if (putStatus.ok()) {
                std::cout << "Blob ID: " << blobIndex.blob_handle.ToString() << std::endl;

                // Truy xuất dữ liệu từ BlobDB
                rocksdb::BlobDB::GetOptions getOptions;
                rocksdb::PinnableSlice readValue;
                rocksdb::Status getStatus = blobDB->Get(getOptions, blobIndex.blob_handle, &readValue);
                if (getStatus.ok()) {
                    std::cout << "Data: " << readValue.ToString() << std::endl;

                    // Xóa dữ liệu từ BlobDB
                    rocksdb::BlobDB::DeleteOptions deleteOptions;
                    rocksdb::Status deleteStatus = blobDB->Delete(deleteOptions, blobIndex.blob_handle);
                    if (!deleteStatus.ok()) {
                        // Xử lý lỗi khi xóa dữ liệu không thành công
                    }
                } else {
                    // Xử lý lỗi khi truy xuất dữ liệu không thành công
                }
            } else {
                // Xử lý lỗi khi thêm dữ liệu không thành công
            }
        } else {
            // Xử lý lỗi khi khởi tạo BlobDB không thành công
        }
    } else {
        // Xử lý lỗi khi khởi tạo RocksDB không thành công
    }
    return 0;
}

RocksDB - MemTable

Trong RocksDB, Memtable là một thành phần quan trọng trong cơ sở dữ liệu. Nó là một bộ nhớ đệm được sử dụng để lưu trữ dữ liệu mới và sắp xếp theo thứ tự khóa. Memtable giúp tăng tốc độ ghi dữ liệu bằng cách tránh việc truy xuất đĩa cứng.

Khi dữ liệu được ghi vào RocksDB, nó sẽ được ghi vào Memtable trước. Memtable sẽ duy trì dữ liệu trong bộ nhớ và sắp xếp theo thứ tự khóa để tối ưu hóa việc truy xuất dữ liệu. Khi Memtable trở nên quá lớn, RocksDB sẽ chuyển đổi dữ liệu từ Memtable vào các tệp SSTable để giải phóng bộ nhớ.

Memtable trong RocksDB có thể được cấu hình để đáp ứng yêu cầu của ứng dụng. Bạn có thể thiết lập kích thước tối đa của Memtable, số lượng Memtable và các thiết lập khác để điều chỉnh hiệu suất và sử dụng bộ nhớ.

Sử dụng Memtable trong RocksDB giúp tăng tốc độ ghi dữ liệu và cải thiện hiệu suất của ứng dụng. Tuy nhiên, điều quan trọng là phải cân nhắc và cấu hình Memtable phù hợp với yêu cầu của ứng dụng để đảm bảo rằng nó không gây ra vấn đề về bộ nhớ hoặc hiệu suất.

Đây là một ví dụ về cách cấu hình Memtable trong RocksDB bằng Java:

import org.rocksdb.*;

public class RocksDBMemtableExample {

    public static void main(String[] args) {
        RocksDB.loadLibrary();
        try (final Options options = new Options().setCreateIfMissing(true);
             final RocksDB db = RocksDB.open(options, "/path/to/database")) {

            // Cấu hình Memtable
            final BlockBasedTableConfig tableConfig = new BlockBasedTableConfig();
            tableConfig.setBlockCacheSize(64 * 1024 * 1024L);
            options.setTableFormatConfig(tableConfig);

            // Ghi dữ liệu vào RocksDB
            byte[] key = "myKey".getBytes();
            byte[] value = "myValue".getBytes();
            db.put(key, value);

            // Đọc dữ liệu từ RocksDB
            byte[] readValue = db.get(key);
            System.out.println("Value: " + new String(readValue));
        } catch (RocksDBException e) {
            // Xử lý lỗi khi thao tác với cơ sở dữ liệu không thành công
        }
    }
}

Trong ví dụ trên, chúng ta sử dụng BlockBasedTableConfig để cấu hình Memtable và bộ nhớ cache. Chúng ta có thể thiết lập kích thước bộ nhớ cache bằng cách sử dụng setBlockCacheSize()

Trong RocksDB, có hai loại Memtable chính là SkipListMemtable và VectorMemtable.

Để cấu hình RocksDB để sử dụng VectorMemtable, bạn có thể sử dụng Options::setMemTableFactoryVector() trong RocksDB C++ API hoặc Options.setMemTableFactoryVector() trong RocksDB Java API.

Việc lựa chọn loại Memtable phù hợp phụ thuộc vào yêu cầu và tải công việc của ứng dụng của bạn. SkipListMemtable là loại Memtable được sử dụng rộng rãi và phổ biến nhất trong RocksDB, và nó thường đáp ứng tốt cho hầu hết các ứng dụng. Tuy nhiên, nếu ứng dụng của bạn đòi hỏi hiệu suất cao hơn hoặc có quy mô dữ liệu lớn, bạn có thể xem xét sử dụng VectorMemtable, mặc dù điều này có thể làm tăng sử dụng bộ nhớ.

RocksDB - Leveled Compaction

Trong RocksDB, Leveled Compaction là một cơ chế quan trọng để quản lý và tổ chức dữ liệu trong cơ sở dữ liệu. Nó được sử dụng để tối ưu hóa việc đọc và ghi dữ liệu bằng cách chia dữ liệu thành các cấp độ (levels) và thực hiện compaction (tái cấu trúc) dữ liệu giữa các cấp độ.

Một cấp độ (level) trong Leveled Compaction bao gồm một tập hợp các tệp SSTable (Sorted String Table) được sắp xếp theo thứ tự khóa. Các cấp độ có kích thước khác nhau và được sắp xếp theo mức độ độc lập với nhau. Cấp độ cao hơn chứa các tệp SSTable lớn hơn và cung cấp tốc độ đọc tốt hơn, trong khi cấp độ thấp hơn có kích thước nhỏ hơn và cung cấp tốc độ ghi tốt hơn.

Khi dữ liệu được ghi vào RocksDB, nó sẽ được ghi vào Memtable trước. Khi Memtable trở nên quá lớn, RocksDB sẽ chuyển đổi dữ liệu từ Memtable vào một cấp độ (level) thấp nhất. Khi một cấp độ (level) trở nên quá lớn, RocksDB sẽ thực hiện quá trình compaction để tái cấu trúc dữ liệu và giảm kích thước của cấp độ đó.

Quá trình compaction trong Leveled Compaction bao gồm hai giai đoạn chính: minor compaction và major compaction.

Leveled Compaction giúp tăng hiệu suất đọc và ghi dữ liệu trong RocksDB bằng cách tối ưu hóa cấu trúc dữ liệu. Nó cũng giúp giảm kích thước của dữ liệu và sử dụng bộ nhớ hiệu quả. Tuy nhiên, việc lựa chọn loại Memtable phù hợp cũng là một yếu tố quan trọng, phụ thuộc vào loại dữ liệu, kích thước bộ nhớ và yêu cầu của ứng dụng cụ thể.

RocksDB - Universal compaction style

Trong RocksDB, Universal Compaction Style là một chiến lược tái cấu trúc dữ liệu được sử dụng để quản lý và tối ưu hóa việc đọc và ghi dữ liệu. Universal Compaction Style cho phép tái cấu trúc dữ liệu giữa các cấp độ (levels) và loại bỏ các tệp SSTable không cần thiết.

Universal Compaction Style sử dụng một loại tệp SSTable đặc biệt gọi là tệp SSTable đa cấp (multi-level SSTable) để lưu trữ dữ liệu. Mỗi tệp SSTable đa cấp bao gồm một hoặc nhiều cấp độ (levels) và sắp xếp dữ liệu theo thứ tự khóa. Các tệp SSTable đa cấp có thể được nén lại và tối ưu hóa để giảm kích thước và tăng hiệu suất đọc dữ liệu.

Khi dữ liệu được ghi vào RocksDB, nó sẽ được ghi vào Memtable trước. Khi Memtable trở nên quá lớn, RocksDB sẽ chuyển đổi dữ liệu từ Memtable vào tệp SSTable đa cấp tại cấp độ thấp nhất. Khi một tệp SSTable đa cấp trở nên quá lớn, RocksDB sẽ thực hiện quá trình compaction để tái cấu trúc dữ liệu và loại bỏ các tệp SSTable không cần thiết.

Quá trình compaction trong Universal Compaction Style cũng bao gồm hai giai đoạn chính: minor compaction và major compaction.

Universal Compaction Style giúp giảm kích thước của dữ liệu và tối ưu hóa hiệu suất đọc và ghi dữ liệu trong RocksDB.

RocksDB - FIFO compaction style

<markdown>

Chi tiết FIFO Compaction Style

Trong RocksDB, FIFO (First-In-First-Out) Compaction Style là một chiến lược tái cấu trúc dữ liệu được sử dụng để quản lý và tối ưu hóa việc đọc và ghi dữ liệu. FIFO Compaction Style tập trung vào việc duy trì thứ tự của các tệp SSTable (Sorted String Table) dựa trên thời gian đến và thời gian sử dụng.

FIFO Compaction Style sử dụng một tệp SSTable đặc biệt gọi là tệp SSTable FIFO để lưu trữ dữ liệu. Tệp SSTable FIFO duy trì thứ tự của các tệp SSTable dựa trên thời gian tạo và thời gian sử dụng. Khi RocksDB nhận một yêu cầu ghi, nó sẽ tạo một tệp SSTable mới và ghi dữ liệu vào đó. Các tệp SSTable sẽ được tổ chức theo thứ tự thời gian, với tệp mới nhất đứng đầu và các tệp cũ hơn đứng sau.

Mục tiêu của FIFO Compaction Style là duy trì sự tuân thủ theo thứ tự của các tệp SSTable. Khi một tệp SSTable cũ đã đạt đến một ngưỡng kích thước hoặc thời gian sử dụng, nó sẽ được xóa hoặc lưu trữ trong một tệp SSTable lưu trữ cố định. Điều này giúp giảm kích thước của RocksDB và làm cho việc truy xuất dữ liệu hiệu quả hơn.

Khi số lượng tệp SSTable trong RocksDB vượt quá một ngưỡng được cấu hình trước, RocksDB sẽ thực hiện quá trình compaction để tái cấu trúc dữ liệu và giảm kích thước của RocksDB. Trong quá trình compaction, RocksDB sẽ chọn một số tệp SSTable và hợp nhất chúng thành một tệp SSTable mới. Quá trình này giúp giảm số lượng tệp SSTable và tối ưu hóa việc đọc dữ liệu.

Tuy nhiên, FIFO Compaction Style không tập trung vào việc tối ưu hóa việc đọc dữ liệu như các chiến lược compaction khác. Thay vào đó, nó tập trung vào việc duy trì thứ tự và xử lý các yêu cầu ghi một cách hiệu quả. Việc lựa chọn chiến lược compaction phù hợp vẫn phụ thuộc vào yếu tố khác như loại dữ liệu, kích thước bộ nhớ và yêu cầu của ứng dụng cụ thể.

Với FIFO Compaction Style, RocksDB có thể cung cấp sự ổn định và đáng tin cậy trong việc lưu trữ và truy xuất dữ liệu. Các tệp SSTable được tổ chức theo thứ tự thời gian, giúp duy trì tính tuần tự và tiện lợi cho các ứng dụng yêu cầu dữ liệu tuần tự. Tuy nhiên, việc lựa chọn chiến lược compaction phù hợp là quan trọng để đảm bảo hiệu suất và tối ưu hóa việc truy cập dữ liệu trong môi trường RocksDB.

RocksDB - SST

Trong RocksDB, SST (Sorted String Table) là định dạng tệp được sử dụng để lưu trữ và quản lý dữ liệu. SST là một tệp nhị phân không chỉ chứa khóa và giá trị, mà còn chứa các thông tin bổ sung như chỉ mục và các thông số khác.

Mỗi tệp SST được chia thành các khoảng giá trị (range) và mỗi khoảng giá trị lại chứa một số lượng khóa và giá trị. Các khoảng giá trị trong tệp SST được sắp xếp theo thứ tự tăng dần của khóa. Khi thực hiện truy vấn dữ liệu, RocksDB sẽ sử dụng các chỉ mục trong tệp SST để tìm kiếm và truy xuất dữ liệu một cách hiệu quả.

Một tệp SST có thể được chia thành các cấp độ (levels) trong các chiến lược compaction khác nhau như Leveled Compaction, Universal Compaction Style, hoặc FIFO Compaction Style. Các tệp SST trong cùng một cấp độ có kích thước tương tự nhau và được sắp xếp theo thứ tự tăng dần của khóa.

SST trong RocksDB cung cấp nhiều lợi ích. Đầu tiên, nó giúp tăng tốc độ truy xuất dữ liệu bằng cách sử dụng chỉ mục và các cấu trúc dữ liệu tối ưu hóa. Thứ hai, nó giúp giảm kích thước của dữ liệu bằng cách nén và lưu trữ dữ liệu một cách hiệu quả. Cuối cùng, nó cung cấp tính bảo mật và độ tin cậy cao cho dữ liệu, vì các tệp SST thường được xử lý và bảo vệ bằng các cơ chế kiểm soát lỗi và sai sót.

Tuy nhiên, việc lựa chọn chiến lược compaction và cấu trúc SST phù hợp là quan trọng để đảm bảo hiệu suất và tối ưu hóa việc truy cập dữ liệu trong môi trường RocksDB. Nên cân nhắc các yếu tố như loại dữ liệu, kích thước bộ nhớ và yêu cầu của ứng dụng cụ thể để chọn phương pháp phù hợp cho việc sử dụng SST trong RocksDB.

RocksDB - Delete Stale Files

Trong RocksDB, Delete Stale Files là một chức năng quan trọng để quản lý và giải phóng tệp SSTable không còn cần thiết. Khi RocksDB thực hiện quá trình compaction và tái cấu trúc dữ liệu, các tệp SSTable cũ và không còn được sử dụng nữa sẽ được xóa để giảm kích thước của RocksDB.

Delete Stale Files hoạt động dựa trên các chỉ mục và thông tin Metadata của RocksDB. Khi RocksDB xác định rằng một tệp SSTable đã không còn sử dụng nữa, nó sẽ đánh dấu tệp đó là "stale" (không còn cần thiết) và thực hiện quá trình xóa.

Ví dụ:

Giả sử bạn có ba tệp SSTable là A, B, và C. Khi RocksDB thực hiện quá trình compaction và tạo ra một tệp mới là D, các tệp cũ A, B, và C sẽ được đánh dấu là stale. Khi RocksDB xác định rằng không có yêu cầu truy vấn dữ liệu nào sử dụng các tệp stale này, nó sẽ thực hiện quá trình Delete Stale Files và xóa các tệp A, B, và C khỏi RocksDB.

Delete Stale Files giúp giảm kích thước của RocksDB và làm cho việc truy cập dữ liệu hiệu quả hơn. Nó đảm bảo rằng chỉ các tệp SSTable cần thiết và được sử dụng mới được giữ lại trong RocksDB.

Ví dụ:

Before Delete Stale Files:
- Tệp SSTable A (stale)
- Tệp SSTable B (stale)
- Tệp SSTable C (stale)
- Tệp SSTable D (active)

After Delete Stale Files:
- Tệp SSTable D (active)

Trong ví dụ trên, sau khi thực hiện Delete Stale Files, chỉ tệp SSTable D còn lại trong RocksDB, trong khi các tệp stale A, B, và C đã được xóa.

RocksDB - Partitioned Index/Filters

Trong RocksDB, Partitioned Index/Filters là một tính năng quan trọng giúp tăng tốc độ truy vấn dữ liệu bằng cách sử dụng các chỉ mục và bộ lọc được chia thành các phân đoạn (partition).

Khi RocksDB tạo ra một tệp SSTable mới, nó sẽ tạo các chỉ mục và bộ lọc tương ứng cho phân đoạn của tệp SSTable đó. Mỗi phân đoạn sẽ có một chỉ mục và bộ lọc riêng, được sắp xếp theo thứ tự tăng dần của khóa.

Khi thực hiện truy vấn dữ liệu, RocksDB sẽ sử dụng các chỉ mục và bộ lọc để xác định phân đoạn chứa khóa cần truy vấn. Việc này giúp giảm số lượng dữ liệu cần xem xét và tăng tốc độ truy xuất dữ liệu.

Ví dụ:

Giả sử bạn có một tệp SSTable với các phân đoạn như sau:

Khi thực hiện truy vấn dữ liệu với khóa X, RocksDB sẽ sử dụng chỉ mục và bộ lọc để xác định rằng phân đoạn 3 chứa khóa X. Vì vậy, RocksDB chỉ cần xem xét dữ liệu trong phân đoạn 3 để tìm kiếm khóa X, giảm bớt thời gian truy xuất dữ liệu.

Partitioned Index/Filters giúp tăng tốc độ truy vấn dữ liệu bằng cách giảm số lượng dữ liệu cần xem xét. Nó cung cấp một cơ chế tối ưu để xác định vị trí của dữ liệu trong RocksDB và tìm kiếm nhanh chóng các khóa cần truy vấn.

Ví dụ:

Giả sử bạn có một RocksDB với các tệp SSTable và phân đoạn như sau:

Khi thực hiện truy vấn dữ liệu với khóa X, RocksDB sẽ sử dụng chỉ mục và bộ lọc của các tệp SSTable để xác định rằng phân đoạn 2 của tệp SSTable 2 chứa khóa X. Vì vậy, RocksDB chỉ cần xem xét dữ liệu trong phân đoạn 2 của tệp SSTable 2 để tìm kiếm khóa X, giảm bớt thời gian

RocksDB - Repairer

Trong RocksDB, RocksDB Repairer là một công cụ được cung cấp để kiểm tra và sửa chữa các tệp SSTable bị hỏng hoặc không hợp lệ trong cơ sở dữ liệu RocksDB. RocksDB Repairer có thể được sử dụng để khắc phục các vấn đề như tệp SSTable bị mất hoặc bị hỏng, chỉ mục không chính xác, hoặc lỗi dữ liệu.

Khi chạy RocksDB Repairer, công cụ sẽ kiểm tra tất cả các tệp SSTable trong RocksDB và xác định các tệp bị hỏng hoặc không hợp lệ. Sau đó, RocksDB Repairer sẽ cố gắng sửa chữa các tệp bị hỏng hoặc không hợp lệ bằng cách sử dụng các thông tin có sẵn trong RocksDB hoặc sử dụng các thông tin phục hồi từ các tệp SSTable khác.

Ví dụ:

Giả sử bạn có một RocksDB với hai tệp SSTable như sau:

Khi chạy RocksDB Repairer trên RocksDB này, công cụ sẽ xác định rằng tệp SSTable 2 bị hỏng hoặc không hợp lệ và cố gắng sửa chữa tệp này bằng cách sử dụng các thông tin phục hồi từ tệp SSTable 1 hoàn chỉnh và các thông tin khác có sẵn trong RocksDB.

RocksDB Repairer là một công cụ hữu ích để khắc phục các vấn đề về tệp SSTable trong RocksDB. Nó giúp đảm bảo tính toàn vẹn và độ tin cậy của dữ liệu trong RocksDB và cung cấp khả năng phục hồi dữ liệu trong trường hợp xảy ra sự cố.

Ví dụ:

Trước khi chạy RocksDB Repairer:
- Tệp SSTable 1 (hoàn chỉnh)
- Tệp SSTable 2 (bị hỏng)

Sau khi chạy RocksDB Repairer:
- Tệp SSTable 1 (hoàn chỉnh)
- Tệp SSTable 2 (đã được sửa chữa)

Trong ví dụ trên, sau khi chạy RocksDB Repairer, tệp SSTable 2 đã được sửa chữa và trở lại trạng thái hoàn chỉnh.

Với RocksDB Repairer, bạn có thể tăng tính toàn vẹn và độ tin cậy của dữ liệu trong RocksDB và xử lý các vấn đề về tệp SSTable một cách hiệu quả.

RocksDB - Write Batch With Index

Trong RocksDB, Write Batch With Index là một tính năng quan trọng để thực hiện các thay đổi vào cơ sở dữ liệu và duy trì chỉ mục của dữ liệu một cách hiệu quả.

Khi thực hiện các thay đổi vào RocksDB, như thêm, cập nhật hoặc xóa dữ liệu, bạn có thể sử dụng Write Batch With Index để nhóm các thay đổi này lại thành một lô (batch). Điều này giúp tăng hiệu suất ghi vào RocksDB bằng cách tránh việc ghi từng thay đổi một cách độc lập.

Write Batch With Index cũng duy trì một chỉ mục để nhanh chóng xác định vị trí của các thay đổi trong lô. Chỉ mục này cho phép RocksDB tìm kiếm và truy xuất dữ liệu một cách hiệu quả ngay cả khi các thay đổi chưa được áp dụng vào cơ sở dữ liệu chính thức.

Ví dụ:

Giả sử bạn muốn thêm hai bản ghi vào RocksDB: (key1, value1) và (key2, value2). Thay vì thực hiện hai lần ghi riêng biệt, bạn có thể sử dụng Write Batch With Index để nhóm hai thay đổi này lại thành một lô (batch).

Khi sử dụng Write Batch With Index, bạn có thể thêm (key1, value1) và (key2, value2) vào bộ nhớ đệm của lô mà không cần thực hiện ghi ngay lập tức. Chỉ khi bạn muốn áp dụng các thay đổi này vào RocksDB, bạn sẽ gọi hàm write() để thực hiện ghi lô (batch) vào RocksDB.

Việc sử dụng Write Batch With Index giúp tăng hiệu suất ghi vào RocksDB bằng cách giảm số lần ghi và tối ưu hóa việc cập nhật chỉ mục của dữ liệu.

Ví dụ:

# Khởi tạo Write Batch With Index
write_batch = rocksdb.WriteBatchWithIndex()

# Thêm các thay đổi vào lô
write_batch.put(b'key1', b'value1')
write_batch.put(b'key2', b'value2')

# Áp dụng lô vào RocksDB
rocksdb.write(write_batch)

Trong ví dụ trên, chúng ta sử dụng Write Batch With Index để nhóm hai thay đổi (key1, value1) và (key2, value2) lại thành một lô. Sau đó, chúng ta áp dụng lô này vào RocksDB bằng cách gọi hàm write().

Write Batch With Index là một tính năng mạnh mẽ trong RocksDB để tăng hiệu suất ghi và duy trì chỉ mục của dữ liệu một cách hiệu quả.

RocksDB - Performance Benchmarks

Kết quả đo tải của RocksDB. Chi tiết: https://github.com/facebook/rocksdb/wiki/Performance-Benchmarks

Setup

All of the benchmarks are run on the same AWS instance. Here are the details of the test setup:

To understand the performance of the SSD card, we ran an fio test and observed 117K IOPS of 4KB reads (See Performance Benchmarks#fio test results for outputs).

All tests were executed against by executing benchmark.sh with the following parameters (unless otherwise specified):

Unless explicitly specified, the remaining tests used default parameters. DIO tests were executed with the options --use_direct_io_for_flush_and_compaction --use_direct_reads.

All other parameters used the default values, unless explicitly mentioned here. Tests were executed sequentially against the same database instance. The db_bench tool was generated via make release.

The following tests were executed in sequence:

Test 1. Bulk Load of keys in Random Order (benchmark.sh bulkload)

NUM_KEYS=900000000 CACHE_SIZE=6442450944 benchmark.sh bulkload

Measure performance to load 900 million keys into the database. The keys are inserted in random order. The database is empty at the beginning of this benchmark run and gradually fills up. No data is being read when the data load is in progress.

Version Opts Time ops/sec mb/sec usec/op p50 p75 p99 p99.9 p99.99 Stall-time Stall% du -s -k
7.2.2 None 4021 1003732 402.0 1.0 0.5 0.8 2 7 22 00:00:52.558 6.3 101406408
7.2.2 DIO 3976 1021386 409.1 1.0 0.5 0.8 2 3 32 00:00:41.215 4.9 101404476
7.1.1 None 3951 1028135 411.8 1.0 0.5 0.8 2 3 21 00:00:42.580 5.1 101407124
7.1.1 DIO 3920 1046129 419.0 1.0 0.5 0.8 2 3 20 00:00:33.023 3.9 101407876
7.0.3 None 3934 1040089 416.6 1.0 0.5 0.8 2 3 22 00:01:02.307 7.4 101406288
7.0.3 DIO 3879 1060242 424.7 0.9 0.5 0.8 2 3 21 00:00:50.523 6.0 101405820
6.29.1 None 3898 1045486 418.8 1.0 0.5 0.8 2 3 55 00:01:17.876 9.3 101405948
6.29.1 DIO 3819 1065706 426.9 0.9 0.5 0.8 2 3 25 00:01:09.405 8.3 101404236
6.29.0 None 3899 1047693 419.6 1.0 0.5 0.8 2 3 108 00:01:25.637 10.2 101407032
6.29.0 DIO 3828 1061703 425.3 0.9 0.5 0.8 2 3 21 00:00:56.298 6.7 101405356
6.28.0 None 3924 1050028 420.6 1.0 0.5 0.8 2 3 60 00:01:17.288 9.2 101406260
6.28.0 DIO 3819 1072892 429.7 0.9 0.5 0.8 2 3 29 00:01:01.648 7.9 101405916
6.27.0 None 3898 1052489 421.6 0.9 0.5 0.8 2 3 22 00:01:07.776 8.1 101406796
6.27.0 DIO 3826 1066941 427.4 0.9 0.5 0.8 2 3 21 00:00:58.306 6.9 101405580
6.26.0 None 3892 1043630 418.0 1.0 0.5 0.8 2 3 54 00:01:17.288 9.2 101407528
6.26.0 DIO 3899 1060561 424.8 0.9 0.5 0.8 2 3 22 00:01:04.536 7.7 101402764
6.25.0 None 3989 1032155 413.4 1.0 0.5 0.8 2 3 102 00:01:23.783 10.0 101407140
6.25.0 DIO 3899 1048824 420.1 1.0 0.5 0.8 2 3 22 00:01:04.747 7.7 101402764
6.24.0 None 3983 1025562 410.8 1.0 0.5 0.8 2 3 32 00:01:12.296 8.6 101406524
6.24.0 DIO 3880 1052049 421.4 1.0 0.5 0.8 2 3 22 00:01:05.862 7.8 101405064
6.23.0 None 4175 1015722 406.8 1.0 0.5 0.8 2 3 69 00:01:17.541 9.2 101405292
6.23.0 DIO 3885 1055232 422.7 0.9 0.5 0.8 2 3 21 00:00:52.360 6.2 101402116
6.22.1 None 4143 1013002 405.8 1.0 0.5 0.8 2 3 224 00:01:26.032 10.2 101405804
6.22.1 DIO 4058 1031703 413.2 1.0 0.5 0.8 2 3 125 00:01:23.019 9.9 101403424
6.21.2 None 4141 1017259 407.5 0.9 0.5 0.8 2 3 556 00:01:32.279 11.0 101406320
6.15.5 None 4068 1045195 418.6 1.0 0.5 0.8 1 3 980 00:02:08.223 15.3 101401808
6.10.4 None 4002 1062310 425.5 0.9 0.5 0.8 1 3 1013 00:02:24.652 17.2 101402936

Test 2. Random Read (benchmark.sh readrandom)

NUM_KEYS=900000000 CACHE_SIZE=6442450944 DURATION=5400 benchmark.sh readrandom

Measure performance to randomly read existing keys. The database after bulkload was used as the starting point.

Version Opts ops/sec mb/sec usec/op p50 p75 p99 p99.9 p99.99
7.2.2 None 136915 34.7 467.4 615.5 772.8 1270 1801 2840
7.2.2 DIO 189236 47.9 338.2 419.6 539.1 1022 1693 2297
7.1.1 None 145490 36.8 439.9 599.7 753.7 1252 1809 2813
7.1.1 DIO 189242 47.9 338.2 419.0 539.1 1037 1696 2294
7.0.3 None 145540 36.8 439.7 599.8 753.3 1251 1803 2803
7.0.3 DIO 189243 47.9 338.2 419.2 539.2 1029 1691 2246
6.29.1 None 145577 36.9 439.6 606.3 751.0 1204 1292 2091
6.29.1 DIO 189243 47.9 338.2 430.0 540.9 854 969 1291
6.29.0 None 145590 36.9 439.6 606.2 751.0 1204 1292 1936
6.29.0 DIO 189241 47.9 338.2 430.0 540.8 854 932 1289
6.28.0 None 146980 37.2 435.4 604.3 748.9 1195 1291 1984
6.28.0 DIO 189232 47.9 338.2 430.0 540.9 854 991 1293
6.27.0 None 146921 37.2 435.6 604.4 748.8 1194 1291 1980
6.27.0 DIO 189250 47.9 338.2 430.1 540.8 854 902 1287
6.26.0 None 128341 32.5 498.7 639.6 805.7 1272 1298 2156
6.26.0 DIO 189244 47.9 338.2 430.1 540.8 854 894 1287
6.25.0 None 128517 32.5 498.0 639.0 804.6 1272 1298 2220
6.25.0 DIO 189245 47.9 338.2 430.1 540.8 854 897 1289
6.24.0 None 130852 33.1 489.1 632.6 791.4 1266 1297 2152
6.24.0 DIO 189240 47.9 338.2 430.0 540.7 854 930 1292
6.23.0 None 137664 34.9 464.9 618.4 766.5 1244 1295 2557
6.23.0 DIO 189252 47.9 338.2 430.0 540.7 854 926 1296
6.22.1 None 138623 35.1 461.7 616.8 763.9 1239 1295 2663
6.22.1 DIO 189237 47.9 338.2 430.0 540.7 854 960 1291
6.21.2 None 138633 35.1 461.6 616.8 764.1 1240 1295 2461
6.15.5 None 138513 35.1 462.0 616.9 764.2 1240 1295 3083
6.10.4 None 138496 35.1 462.1 617.1 764.3 1240 1295 2484

Test 3. Multi-Random Read (benchmark.sh multireadrandom)

NUM_KEYS=900000000 CACHE_SIZE=6442450944 DURATION=5400 benchmark.sh multireadrandom --multiread_batched

Measure performance to randomly multi-get existing keys. The database after bulkload was used as the starting point.

Version Opts ops/sec p50 p75 p99 p99.9 p99.99
7.2.2 None 136928 4657.7 5774.9 9416 9873 18001
7.2.2 DIO 189216 3415.8 4064.2 6422 6586 8602
7.1.1 None 145548 4387.8 5568.6 8899 9831 16943
7.1.1 DIO 189213 3413.7 4064.2 6422 6586 8630
7.0.3 None 145587 4386.7 5567.1 8886 9829 16789
7.0.3 DIO 189230 3413.5 4063.8 6422 6586 8590
6.29.1 None 145652 4376.9 5549.2 8702 9813 15243
6.29.1 DIO 189233 3410.8 4048.6 6406 6583 7498
6.29.0 None 145660 4376.7 5549.1 8701 9811 15305
6.29.0 DIO 189231 3410.3 4048.0 6406 6583 7310
6.28.0 None 147022 4345.6 5523.9 8584 9804 14594
6.28.0 DIO 189228 3410.5 4048.4 6406 6583 7679
6.27.0 None 146989 4346.2 5524.0 8579 9806 15833
6.27.0 DIO 189227 3409.6 4047.2 6405 6583 7332
6.26.0 None 128366 4933.4 6093.6 9672 9884 13845
6.26.0 DIO 189229 3409.0 4046.8 6405 6583 7282
6.25.0 None 128523 4927.2 6087.8 9670 9883 13727
6.25.0 DIO 189241 3408.7 4046.6 6404 6583 7525
6.24.0 None 130859 4836.9 5995.1 9630 9880 14169
6.24.0 DIO 189234 3409.0 4047.2 6406 6584 7996
6.23.0 None 137638 4607.1 5736.4 9360 9869 17172
6.23.0 DIO 189237 3409.0 4047.0 6406 6584 8125
6.22.1 None 138660 461.6 4576.1 5706.3 9294 9867
6.22.1 DIO 189235 338.2 3410.7 4047.7 6406 6583
6.21.2 None 138623 461.7 4577.4 5707.2 9294 9866
6.15.5 None 138507 462.0 4582.3 5710.2 9299 9867
6.10.4 None 138476 462.1 4583.0 5710.9 9298 9864

Test 4. Range Scan (benchmark.sh fwdrange)

NUM_KEYS=900000000 CACHE_SIZE=6442450944 DURATION=5400 benchmark.sh fwdrange

Measure performance to randomly iterate over keys. The database after bulkload was used as the starting point.

Version Opts ops/sec mb/sec usec/op p50 p75 p99 p99.9 p99.99
7.2.2 None 70097 280.8 913.0 791.9 1435.5 1892 2811 10210
7.2.2 DIO 78828 315.7 811.9 836.9 1093.2 1771 2601 2894
7.1.1 None 74491 298.4 859.1 775.3 1380.6 1889 1899 8592
7.1.1 DIO 78831 315.8 811.8 836.7 1093.1 1771 2598 2982
7.0.3 None 74510 298.4 858.9 775.2 1380.9 1889 2786 8384
7.0.3 DIO 78832 315.8 811.8 836.8 1093.1 1771 2603 2895
6.29.1 None 74530 298.5 858.7 775.8 1392.9 1881 1899 7807
6.29.1 DIO 78830 315.7 811.8 870.2 1090.8 1434 1862 2668
6.29.0 None 74535 298.5 858.6 775.7 1393.3 1881 1899 7553
6.29.0 DIO 78832 315.8 811.8 870.3 1090.3 1388 1858 2620
6.28.0 None 75231 301.3 850.7 773.7 1381.2 1880 1899 8224
6.28.0 DIO 78828 315.7 811.9 870.2 1090.8 1438 1862 2655
6.27.0 None 75246 301.4 850.5 773.2 1384.2 1880 1899 8360
6.27.0 DIO 78829 315.7 811.9 870.5 1090.2 1373 1855 2513
6.26.0 None 65717 263.2 973.8 808.2 1492.5 1884 1899 7217
6.26.0 DIO 78831 315.8 811.8 870.5 1090.2 1370 1855 2512
6.25.0 None 65813 263.6 972.4 807.7 1491.5 1884 1899 7338
6.25.0 DIO 78833 315.8 811.8 870.4 1090.2 1369 1856 2610
6.24.0 None 67004 268.4 955.1 802.8 1480.1 1884 1899 7216
6.24.0 DIO 78832 315.8 811.8 870.4 1090.2 1376 1856 2582
6.23.0 None 70459 282.2 908.3 789.6 1443.0 1883 1899 10273
6.23.0 DIO 78829 315.7 811.9 870.5 1090.1 1356 1855 2596
6.22.1 None 70971 284.3 901.7 787.8 1437.2 1882 1899 10274
6.22.1 DIO 78829 315.7 811.9 870.3 1090.5 1411 1859 2618
6.21.2 None 70967 284.3 901.8 787.8 1437.3 1882 1899 10253
6.15.5 None 70978 284.3 901.7 787.7 1437.5 1882 1899 9890
6.10.4 None 70973 284.3 901.7 787.6 1438.0 1882 1899 9945

Test 4b. Reverse Range Scan (benchmark.sh revrange)

NUM_KEYS=900000000 CACHE_SIZE=6442450944 DURATION=5400 benchmark.sh revrange

Measure performance to randomly iterate over keys. The database after bulkload was used as the starting point.

Version Opts ops/sec mb/sec usec/op p50 p75 p99 p99.9 p99.99
7.2.2 None 68785 275.5 930.4 806.0 1467.2 1892 2859 12052
7.2.2 DIO 76200 305.2 839.9 897.5 1114.2 1776 2617 2898
7.1.1 None 73116 292.9 875.3 788.1 1399.9 1889 2853 13338
7.1.1 DIO 76202 305.2 839.8 897.1 1114.1 1778 2631 3022
7.0.3 None 73149 293.0 874.9 788.0 1399.4 1889 2853 13524
7.0.3 DIO 76202 305.2 839.8 897.4 1114.0 1776 2632 3173
6.29.1 None 73167 293.1 874.7 788.9 1406.9 1882 1900 12818
6.29.1 DIO 76204 305.2 839.8 910.2 1112.1 1562 1874 2764
6.29.0 None 73170 293.1 874.6 788.6 1409.1 1882 1899 12688
6.29.0 DIO 76202 305.2 839.8 910.2 1111.5 1524 1870 2722
6.28.0 None 73839 295.8 866.7 786.5 1391.8 1881 1900 13492
6.28.0 DIO 76205 305.2 839.8 910.0 1112.1 1560 1873 2715
6.27.0 None 73861 295.8 866.5 786.0 1396.4 1881 1899 13488
6.27.0 DIO 76204 305.2 839.8 910.3 1111.3 1510 1869 2718

Test 5. Overwrite (benchmark.sh overwrite)

NUM_KEYS=900000000 CACHE_SIZE=6442450944 DURATION=5400 benchmark.sh overwrite

Measure performance to randomly overwrite keys into the database. The database was first created by the previous benchmark.

Version Opts ops/sec mb/sec W-Amp W-MB/s usec/op p50 p75 p99 p99.9 p99.99 Stall-time Stall% du -s -k
7.2.2 None 86617 34.7 9.5 149.7 738.9 449.7 777.6 10479 30005 58328 00:04:43.188 5.3 158540048
7.2.2 DIO 86839 34.8 9.4 154.6 737.0 460.7 775.4 9534 29149 54278 00:03:00.102 3.4 159135832
7.1.1 None 90203 36.1 9.4 155.4 709.5 418.5 746.0 10469 30015 58380 00:04:45.549 5.3 160992944
7.1.1 DIO 88590 35.5 9.6 154.6 722.4 440.2 754.0 9538 29313 55494 00:04:15.453 4.8 158372164
7.0.3 None 90985 36.4 9.4 155.8 703.4 418.2 743.9 10156 29987 57788 00:04:48.049 5.3 161110716
7.0.3 DIO 89686 35.9 9.5 154.1 713.6 439.3 752.3 9377 20921 53505 00:03:28.796 3.9 160356720
6.29.1 None 90711 36.3 9.4 155.1 705.5 418.2 740.0 10213 29779 57100 00:05:28.848 6.1 161099792
6.29.1 DIO 89469 35.8 9.5 154.4 715.3 431.6 748.0 9568 29143 54324 00:04:06.106 4.6 159661172
6.29.0 None 89373 35.8 9.6 155.3 716.1 434.1 756.1 10447 29891 57952 00:04:21.622 4.9 158912856
6.29.0 DIO 88517 35.5 9.5 152.6 723.0 455.1 759.1 9219 28694 51750 00:03:31.235 3.9 160258772
6.28.0 None 89791 36.0 9.4 153.7 712.4 430.9 751.9 10292 29839 58737 00:04:15.276 4.8 161859856
6.28.0 DIO 88108 35.3 9.5 152.4 726.4 449.4 763.5 9508 28917 54122 00:03:25.719 3.8 159865440
6.27.0 None 89815 36.0 9.5 154.1 712.6 427.7 749.4 10533 29660 57615 00:04:30.399 5.0 160273772
6.27.0 DIO 88440 35.4 9.4 151.6 723.6 455.0 761.2 9383 28764 52844 00:03:20.977 3.7 159572484
6.26.0 None 90340 36.2 9.4 153.6 708.4 430.2 742.5 10198 29692 55635 00:04:54.193 5.5 161202432
6.26.0 DIO 88401 35.4 9.6 154.5 724.0 446.0 754.6 9418 28911 52526 00:03:50.428 4.3 158469672
6.25.0 None 89567 35.9 9.4 155.2 714.5 419.4 742.7 10327 29952 59957 00:05:52.335 6.5 160392244
6.25.0 DIO 88549 35.5 9.5 153.6 722.7 433.9 743.6 9483 29064 54109 00:05:00.728 5.6 158500488
6.24.0 None 90829 36.4 4.7 155.2 704.6 397.1 726.4 10359 29968 58160 00:07:01.849 7.9 160757048
6.24.0 DIO 90105 36.1 4.8 153.7 710.3 421.8 736.9 9344 28869 52676 00:05:22.128 6.0 160833572
6.23.0 None 89052 35.7 4.7 151.3 718.7 442.5 758.8 10263 29763 53874 00:04:40.429 5.2 160633196
6.23.0 DIO 88624 35.5 4.9 152.4 722.1 441.5 749.0 9319 28887 53792 00:04:53.783 5.5 158994508
6.22.1 None 91586 36.7 4.7 155.0 698.8 380.5 709.4 10140 29887 58244 00:08:29.153 9.5 161321740
6.22.1 DIO 90310 36.2 4.8 154.7 708.7 419.0 730.1 9227 28816 55513 00:06:22.790 7.1 160400436
6.21.2 None 91776 36.8 4.7 155.6 697.3 379.9 708.7 10055 29782 55942 00:08:24.882 9.4 162082088
6.15.5 None 92911 37.2 4.7 158.4 688.8 351.9 697.7 10031 29894 58333 00:08:43.334 9.7 161156844
6.10.4 None 94539 37.9 4.7 161.9 676.9 328.4 700.4 10022 29843 56548 00:07:11.226 8.0 162965216

Test 6. Multi-threaded read and single-threaded write (benchmark.sh readwhilewriting)

NUM_KEYS=900000000 CACHE_SIZE=6442450944 DURATION=5400 MB_WRITE_PER_SEC=2 benchmark.sh readwhilewriting

Measure performance with one writer and multiple reader threads. The writes are rate limited.

Version Opts ops/sec mb/sec W-Amp W-MB/s usec/op p50 p75 p99 p99.9 p99.99 du -s -k
7.2.2 None 98240 31.1 18.1 11.4 651.4 600.6 829.8 3963 6041 10139 140646588
7.2.2 DIO 143283 45.3 17.1 7.3 446.7 394.8 539.8 2820 4315 6393 140470436
7.1.1 None 102056 32.5 16.9 10.6 627.1 584.8 803.2 3931 6031 9844 141627716
7.1.1 DIO 142958 45.3 17.9 7.6 447.7 395.6 540.1 2819 4316 6405 140849884
7.0.3 None 101948 32.5 17.0 10.7 627.7 585.6 803.6 3931 6028 9824 141767112
7.0.3 DIO 142923 45.4 18.2 7.8 447.8 393.0 539.4 2825 4322 6414 141164436
6.29.1 None 100445 31.8 28.3 18.2 637.1 593.0 810.3 3906 6524 18544 140795968
6.29.1 DIO 141799 44.8 31.5 14.2 451.3 397.1 541.4 2792 4744 9095 140017864
6.29.0 None 100853 31.9 27.7 17.7 634.6 592.9 810.8 3893 6480 17827 140416272
6.29.0 DIO 141947 44.8 32.6 14.4 450.9 397.4 542.1 2786 4791 9273 139972676
6.28.0 None 101233 32.0 28.3 18.3 632.2 591.6 807.0 3892 6530 19073 140616192
6.28.0 DIO 141854 44.7 35.0 15.2 451.2 394.1 541.4 2796 5000 9696 139803484
6.27.0 None 101375 32.1 27.7 17.8 631.3 587.9 805.0 3893 6477 18216 140673616
6.27.0 DIO 142460 44.9 31.2 14.1 449.2 394.7 539.6 2789 4685 9127 139867840
6.26.0 None 91879 29.1 27.7 19.0 696.5 630.8 904.6 4010 6424 13872 140615968
6.26.0 DIO 142148 44.8 31.8 14.4 450.2 394.7 540.0 2793 4697 8939 139826380
6.25.0 None 91736 29.0 28.7 20.0 697.6 630.8 906.2 4019 6418 13775 140615968
6.25.0 DIO 141618 44.7 33.0 14.9 451.9 394.4 540.8 2800 4825 9113 140031428
6.24.0 None 92974 29.5 27.6 19.0 688.3 624.8 869.7 4010 6436 14558 140384360
6.24.0 DIO 141491 44.7 32.7 15.0 452.3 395.8 540.8 2802 4867 9311 140255568
6.23.0 None 96811 30.6 29.1 18.9 661.1 607.3 835.3 3966 6433 13513 140384360
6.23.0 DIO 142410 44.9 29.6 13.5 449.4 394.0 539.3 2789 4598 8989 139961824
6.22.1 None 96812 30.7 28.4 18.5 661.1 606.5 832.8 3958 6500 15777 140972560
6.22.1 DIO 140635 44.5 32.5 14.9 455.1 400.4 543.4 2804 5051 9348 140465744
6.21.2 None 96891 30.7 29.1 18.9 660.5 607.1 833.4 3961 6465 13669 141208940
6.15.5 None 96223 30.6 28.0 18.7 665.1 609.4 835.1 3965 6475 15613 141339712
6.10.4 None 95649 30.5 30.2 19.7 669.1 608.1 834.5 3999 6597 17861 141636760

Test 7. Multi-threaded scan and single-threaded write (benchmark.sh fwdrangewhilewriting)

NUM_KEYS=900000000 CACHE_SIZE=6442450944 DURATION=5400 MB_WRITE_PER_SEC=2 benchmark.sh fwdrangewhilewriting

Measure performance with one writer and multiple iterator threads. The writes are rate limited.

Version Opts ops/sec mb/sec W-Amp W-MB/s usec/op p50 p75 p99 p99.9 p99.99 du -s -k
7.2.2 None 40675 162.9 17.4 7.4 1573.4 1374.6 1855.1 6293 13434 24996 141346104
7.2.2 DIO 35619 142.7 18.3 7.5 1796.6 1540.7 2171.8 6533 9698 13325 140957044
7.1.1 None 42202 169.0 16.5 7.3 1516.4 1322.2 1821.4 6168 13098 25099 142336676
7.1.1 DIO 35535 142.3 17.9 7.3 1800.8 1544.5 2175.7 6527 9691 13298 141591172
7.0.3 None 42436 170.0 17.3 7.8 1508.0 1310.0 1815.5 6198 13226 24937 142579812
7.0.3 DIO 35702 143.0 18.9 7.8 1792.5 1535.0 2165.4 6531 9702 13343 141636716
6.29.1 None 43138 172.8 17.6 7.8 1483.5 1294.3 1804.6 6089 13026 25065 141561940
6.29.1 DIO 36460 146.0 16.5 7.1 1755.2 1517.8 2128.9 6381 9572 12979 140761644
6.29.0 None 42806 171.5 17.0 7.6 1495.0 1311.0 1813.2 6101 13108 25308 140416272
6.29.0 DIO 36418 145.9 17.4 7.4 1757.2 1522.1 2124.5 6404 9624 13210 140619752
6.28.0 None 43564 174.5 17.3 7.7 1469.0 1282.2 1794.6 6055 12926 24865 141241492
6.28.0 DIO 36230 145.1 17.7 7.5 1766.3 1527.9 2142.4 6439 9653 13251 140537532
6.27.0 None 43229 173.2 18.3 8.3 1480.4 1290.3 1802.6 6123 13140 24987 141261580
6.27.0 DIO 35860 143.6 16.9 7.4 1784.5 1540.3 2181.7 6422 9603 13041 140542060
6.26.0 None 36960 148.0 17.2 8.2 1731.4 1534.2 2217.9 6477 13239 21533 141557396
6.26.0 DIO 35961 144.0 16.5 7.3 1779.5 1536.8 2174.2 6415 9603 13055 140627180
6.25.0 None 37344 149.6 17.9 8.4 1713.7 1513.5 2164.7 6489 13409 21654 141269488
6.25.0 DIO 36023 144.3 17.9 7.8 1776.5 1532.5 2162.0 6458 9672 13308 140685060
6.24.0 None 38940 156.0 17.7 8.1 1643.4 1445.8 1970.8 6411 13480 21757 141724476
6.24.0 DIO 35955 144.0 17.1 7.5 1779.8 1534.8 2173.0 6427 9615 13093 140989196
6.23.0 None 41322 165.5 16.7 7.6 1584.7 1359.9 1838.4 6225 13285 24338 141101776
6.23.0 DIO 35968 144.1 17.0 7.4 1779.2 1536.8 2167.6 6446 9648 13218 140731428
6.22.1 None 41244 165.2 16.7 7.6 1551.6 1362.3 1845.8 6234 13346 24988 141716340
6.22.1 DIO 35962 144.0 17.0 7.4 1779.5 1538.8 2150.6 6455 9661 13264 141008104
6.21.2 None 41360 165.7 18.2 8.0 1547.2 1354.1 1840.5 6257 13434 25280 141820100
6.15.5 None 42197 169.0 17.5 8.0 1516.6 1315.5 1817.8 6185 13018 23081 142157224
6.10.4 None 41827 167.5 17.6 8.0 1530.0 1329.2 1826.2 6212 13129 23244 142497356

Test 7b. Multi-threaded scan and single-threaded write (benchmark.sh revrangewhilewriting)

NUM_KEYS=900000000 CACHE_SIZE=6442450944 DURATION=5400 MB_WRITE_PER_SEC=2 benchmark.sh revrangewhilewriting

Measure performance with one writer and multiple iterator threads. The writes are rate limited.

Version Opts ops/sec mb/sec W-Amp W-MB/s usec/op p50 p75 p99 p99.9 p99.99 du -s -k
7.2.2 None 33680 134.9 17.3 7.5 1900.1 1668.2 2417.1 7207 16605 29880 142066536
7.2.2 DIO 31215 125.0 16.4 6.9 2050.0 1755.3 2528.0 7637 10178 13981 141817860
7.1.1 None 34825 139.5 17.4 7.7 1837.6 1623.9 2360.8 6742 16569 30135 142975980
7.1.1 DIO 31259 125.2 17.4 7.3 2047.2 1744.1 2520.7 7673 10205 13980 142349268
7.0.3 None 35015 140.3 17.5 7.8 1827.6 1614.4 2345.4 6695 16540 29947 143211480
7.0.3 DIO 31155 124.8 15.9 7.0 2054.0 1753.5 2529.0 7627 9970 13948 142568752
6.29.1 None 35535 142.3 17.6 7.8 1800.8 1598.9 2320.1 6572 16547 30132 142191812
6.29.1 DIO 31839 127.5 16.8 7.3 2009.9 1731.9 2489.0 7184 9882 13917 141520096
6.29.0 None 35676 142.9 17.3 7.8 1793.8 1592.1 2307.6 6569 16711 30812 141867556
6.29.0 DIO 31896 127.8 17.8 7.8 2006.3 1728.1 2483.7 7320 10017 13938 141162940
6.28.0 None 35882 143.7 16.6 7.5 1783.4 1588.0 2292.4 6534 16405 30385 142078076
6.28.0 DIO 31855 127.6 16.5 7.3 2008.9 1727.1 2485.2 7287 10012 14000 141298660
6.27.0 None 35594 142.6 17.0 7.7 1797.9 1596.8 2315.0 6566 16418 29917 142075232
6.27.0 DIO 32086 128.5 17.0 7.4 1994.4 1717.5 2470.2 7116 9865 13867 141261580

Appendix

fio test results

]$ fio --randrepeat=1 --ioengine=sync --direct=1 --gtod_reduce=1 --name=test --filename=/data/test_file --bs=4k --iodepth=64 --size=4G --readwrite=randread --numjobs=32 --group_reporting
test: (g=0): rw=randread, bs=4K-4K/4K-4K/4K-4K, ioengine=sync, iodepth=64
...
fio-2.14
Starting 32 processes
Jobs: 3 (f=3): [_(3),r(1),_(1),E(1),_(10),r(1),_(13),r(1),E(1)] [100.0% done] [445.3MB/0KB/0KB /s] [114K/0/0 iops] [eta 00m:00s]
test: (groupid=0, jobs=32): err= 0: pid=28042: Fri Jul 24 01:36:19 2020
  read : io=131072MB, bw=469326KB/s, iops=117331, runt=285980msec
  cpu          : usr=1.29%, sys=3.26%, ctx=33585114, majf=0, minf=297
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=33554432/w=0/d=0, short=r=0/w=0/d=0, drop=r=0/w=0/d=0
     latency   : target=0, window=0, percentile=100.00%, depth=64

Run status group 0 (all jobs):
   READ: io=131072MB, aggrb=469325KB/s, minb=469325KB/s, maxb=469325KB/s, mint=285980msec, maxt=285980msec

Disk stats (read/write):
  nvme1n1: ios=33654742/61713, merge=0/40, ticks=8723764/89064, in_queue=8788592, util=100.00%

]$ fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=test --filename=/data/test_file --bs=4k --iodepth=64 --size=4G --readwrite=randread
test: (g=0): rw=randread, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=64
fio-2.14
Starting 1 process
Jobs: 1 (f=1): [r(1)] [100.0% done] [456.3MB/0KB/0KB /s] [117K/0/0 iops] [eta 00m:00s]
test: (groupid=0, jobs=1): err= 0: pid=28385: Fri Jul 24 01:36:56 2020
  read : io=4096.0MB, bw=547416KB/s, iops=136854, runt=  7662msec
  cpu          : usr=22.20%, sys=48.81%, ctx=144112, majf=0, minf=73
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0%
     issued    : total=r=1048576/w=0/d=0, short=r=0/w=0/d=0, drop=r=0/w=0/d=0
     latency   : target=0, window=0, percentile=100.00%, depth=64

Run status group 0 (all jobs):
   READ: io=4096.0MB, aggrb=547416KB/s, minb=547416KB/s, maxb=547416KB/s, mint=7662msec, maxt=7662msec

Disk stats (read/write):
  nvme1n1: ios=1050868/1904, merge=0/1, ticks=374836/2900, in_queue=370532, util=98.70%