Redis và Redis Stack

Redis và Redis Stack

Giới thiệu

Redis(Remote Dictionary Server) là 1 trong số các hệ quản trị cơ sở dữ liệu mang phong cách NoSQL.

Dùng để lưu trữ dữ liệu có cấu trúc dưới dạng key-value trên RAM với hiệu năng cao.

Có thể sử dụng như một database, bộ nhớ cache hay một message broker. Redis cũng có thể được sử dụng như một database được lưu trữ trong RAM để tăng tốc độ xử lí.

Redis hiện cung cấp thời gian phản hồi ở tốc độ chưa đến một mili giây, vì Redis cho phép bạn lưu trữ trên RAM của mình nhanh hơn so với khi lưu trữ trên các ổ cứng, khoảng 150.000 lần so với truy cập ổ HDD và nhanh hơn 500 lần so với truy cập SSD.

Sử dụng tốt trong việc liên kết các microservices với nhau.

Đặc trưng cơ bản

Data model

Redis không có bảng. Redis lưu trữ data dưới dạng key-value.

Các kiểu dữ liệu Redis dùng để lưu value:
STRING: Có thể là string, integer hoặc float. Redis có thể làm việc với cả string, từng phần của string, cũng như tăng/giảm giá trị của integer, float.

LIST: Danh sách liên kết của các strings. Redis hỗ trợ các thao tác push, pop từ cả 2 phía của list, trim dựa theo offset, đọc 1 hoặc nhiều items của list, tìm kiếm và xóa giá trị.

SET: Tập hợp các string (không được sắp xếp). Redis hỗ trợ các thao tác thêm, đọc, xóa từng phần tử, kiểm tra sự xuất hiện của phần tử trong tập hợp. Ngoài ra Redis còn hỗ trợ các phép toán tập hợp, gồm intersect/union/difference.

HASH: Lưu trữ hash table của các cặp key-value, trong đó key được sắp xếp ngẫu nhiên, không theo thứ tự nào cả. Redis hỗ trợ các thao tác thêm, đọc, xóa từng phần tử, cũng như đọc tất cả giá trị.

ZSET (sorted set): Là 1 danh sách, trong đó mỗi phần tử là map của 1 string (member) và 1 floating-point number (score), danh sách được sắp xếp theo score này. Redis hỗ trợ thao tác thêm, đọc, xóa từng phần tử, lấy ra các phần tử dựa theo range của score hoặc của string.

Replication – Persistence:

Redis sử dụng kiến trúc primary-replica và hỗ trợ sao chép không đồng bộ, dữ liệu có thể được sao chép sang nhiều máy chủ khác nhau. Điều này giúp hiệu suất đọc được cải thiện (vì các yêu cầu có thể được phân chia giữa các máy chủ) và phục hồi nhanh hơn khi máy chủ chính gặp sự cố ngừng hoạt động. Để duy trì, Redis hỗ trợ sao lưu point-in-time backups (sao chép dữ liệu Redis vào đĩa).

In-Memory Data Store

Redis lưu trữ dữ liệu trên RAM nên việc đọc ghi dữ liệu nhanh hơn nhiều so với trên ổ cứng.

Nhưng cũng vì toàn bộ dữ liệu trên RAM nên sẽ bị mất khi tắt server. Tuy nhiên, đã có giải pháp là sử dụng Redis Persistence.

Persistent redis

Dù làm việc với data dạng key-value lưu trữ trên RAM, Redis vẫn cần lưu trữ dữ liệu trên ổ cứng để đảm bảo toàn vẹn dữ liệu khi có sự cố xảy ra (server bị tắt nguồn) cũng như tái tạo lại dataset khi restart server. Redis cung cấp 2 phương thức chính cho việc sao lưu dữ liệu ra ổ cứng, đó là RDB và AOF.

RDB (Redis Database)

RDB thực hiện tạo và sao lưu snapshot của DB vào ổ cứng sau mỗi khoảng thời gian nhất định.

Ưu điểm của RDB
RDB cho phép người dùng lưu các version khác nhau của DB, rất thuận tiện khi có sự cố xảy ra.

RDB giúp tối ưu hóa hiệu năng của Redis. Tiến trình Redis chính sẽ chỉ làm các công việc trên RAM, bao gồm các thao tác cơ bản được yêu cầu từ phía client như thêm/đọc/xóa, trong khi đó 1 tiến trình con sẽ đảm nhiệm các thao tác disk I/O. Cách tổ chức này giúp tối đa hiệu năng của Redis.

Nhược điểm của RDB
Thông thường người dùng sẽ set up để tạo RDB snapshot 5 phút 1 lần (hoặc nhiều hơn). Do vậy, trong trường hợp có sự cố, Redis không thể hoạt động, dữ liệu trong những phút cuối sẽ bị mất.

RDB cần dùng fork để tạo tiến trình con phục vụ cho thao tác disk I/O. Trong trường hợp dữ liệu quá lớn, quá trình fork có thể tốn thời gian và server sẽ không thể đáp ứng được request từ client trong vài milisecond hoặc thậm chí là 1 second tùy thuộc vào lượng data và hiệu năng CPU.

AOF (Append Only File)

AOF lưu lại tất cả các thao tác write mà server nhận được, các thao tác này sẽ được chạy lại khi restart server hoặc tái thiết lập dataset ban đầu.

Ưu điểm của AOF
AOF Redis đảm bảo dataset được bền vững hơn. Người dùng có thể config để Redis ghi log mỗi giây 1 lần hoặc ở mọi truy vấn.

Redis ghi log AOF theo kiểu thêm vào cuối file sẵn có, do đó tiến trình seek trên file có sẵn là không cần thiết. Ngay cả khi nhật ký kết thúc bằng một lệnh được viết một nửa vì lý do nào đó (đĩa đầy hoặc các lý do khác), công cụ redis-check-aof vẫn có thể khắc phục nó một cách dễ dàng.

Redis cung cấp tiến trình chạy nền, cho phép ghi lại file AOF khi dung lượng file quá lớn. Việc ghi lại hoàn toàn an toàn vì trong khi Redis tiếp tục thêm vào tệp cũ, một tệp hoàn toàn mới được tạo ra với tập hợp các thao tác tối thiểu cần thiết để tạo tập dữ liệu hiện tại và khi tệp thứ hai này đã sẵn sàng, Redis chuyển hai tệp và bắt đầu thêm vào một file mới.

Nhược điểm của AOF
Các tệp AOF thường lớn hơn các tệp RDB tương đương cho cùng một tập dữ liệu.

AOF có thể chậm hơn RDB tùy thuộc vào chính sách fsync. Nói chung với fsync được đặt thành từng giây , hiệu suất vẫn rất cao và với fsync bị vô hiệu hóa, nó sẽ chính xác nhanh như RDB. RDB vẫn có thể cung cấp nhiều đảm bảo hơn về độ trễ tối đa ngay cả trong trường hợp tải ghi rất lớn.

Hiệu suất

So với cơ sở dữ liệu trên ổ đĩa truyền thống trong đó phần lớn các tác vụ đều yêu cầu truy cập lặp lại tới ổ đĩa, còn Redis thì chỉ việc lấy ra kết quả đã tính toán. Do đó hiệu suất Redis nhanh rõ rệt với các tác vụ đọc hoặc ghi thông thường cùng với độ trễ thấp và thông lượng cao.

Hỗ trợ các thao tác nhiều hơn và thời gian phản hồi nhanh hơn gấp 10 lần. Các thao tác đọc và ghi trung bình chỉ mất chưa đến một mili giây và hỗ trợ hàng triệu thao tác mỗi giây.

Trường hợp sử dụng phổ biến của Redis

Lưu trữ bộ nhớ đệm

Lưu danh sách tác vụ chờ xử lý

Bảng xếp hạng game

Kho lưu trữ session

Machine Learning

Phân tích theo thời gian thực

Redis Stack

Redis Stack được tạo ra để cho phép các lập trình viên xây dựng các ứng dụng thời gian thực với nền tảng dữ liệu phụ trợ có thể xử lý các yêu cầu một cách đáng tin cậy trong vài mili giây bằng cách mở rộng Redis với các mô hình dữ liệu và các công cụ xử lý dữ liệu.

Redis Stack có 5 gói module: RedisJSON, RediSearch, RedisGraph, RedisTimeSeriesRedisBloom.

Ngoài ra, Redis Stack cũng bao gồm RedisInsight, một công cụ trực quan hóa để hiểu và tối ưu hóa dữ liệu.

Giao diện RedisInsight

Để dễ dàng hiểu rõ cách thức hoạt động của Redis Stack, chúng ta sẽ bắt đầu tìm hiểu cách sử dụng 2 module RedisJSONRediSearch với thư viện node-redis.

Cài đặt thư viện node-redis

npm install redis

Kết nối đến Redis Server

import { createClient } from 'redis';

const client = createClient({
  url: 'redis[s]://[[username][:password]@][host][:port][/db-number]'
});

client.on('error', (err) => console.log('Redis Client Error', err));

await client.connect();

RedisJSON

RedisJSON là một module Redis cung cấp hỗ trợ JSON trong Redis. RedisJSON cho phép bạn lưu trữ, cập nhật và truy xuất các giá trị JSON trong Redis giống như cách bạn làm với bất kỳ kiểu dữ liệu Redis nào khác. RedisJSON cũng hoạt động đồng thời với RediSearch để cho phép bạn lập chỉ mục và truy vấn các tài liệu JSON của mình.

Tài liệu được lưu trữ dưới dạng dữ liệu nhị phân trong cấu trúc cây, cho phép truy cập nhanh vào các phần tử con.

Lưu trữ tài liệu JSON trong Redis

Lệnh JSON.SETlưu trữ một giá trị JSON tại một Đường dẫn JSON nhất định trong một khóa Redis.

Ở đây, chúng ta sẽ lưu trữ một tài liệu JSON trong thư mục gốc của khóa Redis ” users“:

await client.json.set('noderedis:users', '$', {
    name: 'Alice',
    age: 29
});

Truy xuất tài liệu JSON từ Redis

const results = await client.json.get('noderedis:users');

Kết quả results trả về

{ name: 'Alice', age: 29 }

RediSearch

RediSearch là một module Redis cung cấp khả năng truy vấn, lập index phụ và tìm kiếm cho Redis. Để sử dụng RediSearch, trước tiên bạn khai báo các chỉ mục trên dữ liệu Redis của mình. Sau đó, bạn có thể sử dụng ngôn ngữ truy vấn RediSearch để truy vấn dữ liệu đó.

RediSearch sử dụng các index được nén, đảo ngược để lập index nhanh với dung lượng bộ nhớ thấp.

RediSearch cung cấp khả năng so sánh cụm từ chính xác, fuzzy search (tìm kiếm “xấp xỉ”), numeric filtering,…

Lập chỉ mục và truy vấn dữ liệu trong Redis Hashes

Tạo chỉ mục

Trước khi có thể thực hiện bất kỳ tìm kiếm nào, chúng ta cần cho RediSearch biết cách lập chỉ mục dữ liệu và key nào để tìm dữ liệu đó. Lệnh FT.CREATE tạo chỉ mục RediSearch. Đây là cách sử dụng nó để tạo một chỉ mục mà ta sẽ gọi idx:animalsnơi chúng ta muốn lập chỉ mục các hash chứa và trường name, species, age cùng tên khóa của chúng trong Redis bắt đầu bằng tiền tố :noderedis:animals

await client.ft.create('idx:animals', {
  name: {
    type: SchemaFieldTypes.TEXT,
    sortable: true
  },
    species: SchemaFieldTypes.TAG,
    age: SchemaFieldTypes.NUMERIC
  }, {
    ON: 'HASH',
    PREFIX: 'noderedis:animals'
  }
);

Sau khi bạn tạo chỉ mục, mọi tài liệu băm mới có tiền tố tương ứng sẽ tự động được lập chỉ mục khi tạo.

Thêm tài liệu

Sử dụng lệnh HSET để tạo một tài liệu băm mới và thêm nó vào chỉ mục:

await client.hSet('noderedis:animals:1', {name: 'Fluffy', species: 'cat', age: 3});
await client.hSet('noderedis:animals:2', {name: 'Ginger', species: 'dog', age: 4});

Tìm kiếm chỉ mục

Để tìm kiếm chỉ mục cho các tài liệu có chứa các từ cụ thể, sử dụng FT.SEARCH

const results = await client.ft.search(
    'idx:animals', 
    '@species:{dog}',
    {
      SORTBY: {
        BY: 'age',
        DIRECTION: 'DESC' // or 'ASC' (default if DIRECTION is not present)
      }
    }
  );

results sẽ trông như thế này:

{
  total: 1,
  documents: [
    {
      id: 'noderedis:animals:2',
      value: {
        name: 'Ginger',
        species: 'dog',
        age: '4'
      }
    }
  ]
}
Lập chỉ mục và truy vấn dữ liệu với RedisJSON

Tạo chỉ mục

Để tạo chỉ mục cho một tài liệu JSON

await client.ft.create('idx:users', {
    '$.name': {
        type: SchemaFieldTypes.TEXT,
        SORTABLE: 'UNF'
    },
    '$.age': {
        type: SchemaFieldTypes.NUMERIC,
        AS: 'age'
    },
}, {
    ON: 'JSON',
    PREFIX: 'noderedis:users'
});

Truy vấn chỉ mục

Chúng ta sẽ sử dụng lệnh FT.SEARCH và ngôn ngữ truy vấn RediSearchVí du: một truy vấn để tìm người dùng dưới 30 tuổi:

const results = await client.ft.search('idx:users', '@age:[0 30]');

Kết quả results trả về

{
  "total": 1,
  "documents": [
    {
      "id": "noderedis:users",
      "value": {
        "name": "Alice",
        "age": 29
      }
    }
  ]
}

Kết luận

Redis là một gói phần mềm mã nguồn mở với nhiều ưu điểm nên đang được sử dụng phổ biến trong quá trình phát triển phần mềm.

Có tốc độ cực nhanh, dễ thiết lập và sử dụng, hỗ trợ nhiều cấu trúc dữ liệu linh hoạt. Redis là sự lựa chọn tuyệt vời khi cần sử dụng để cache dữ liệu hoặc các ứng dụng có yêu cầu về thời gian thực.

Với cùng cơ chế lưu trữ dữ liệu trên RAM, so với Memcached, Redis hỗ trợ nhiều kiểu dữ liệu và nhiều phép toán hơn ở phía server. Đồng thời cũng có cơ chế mặc định backup dữ liệu vào disk trong khi Memcached không có cơ chế này. Vì vậy nếu bạn muốn hệ thống của mình có thể cache được nhưng cấu trúc dữ liệu phức tạp thì Redis sẽ là một lựa chọn tốt hơn.

Tài liệu tham khảo

https://redis.io/docs/

https://redis.js.org/

https://byterot.blogspot.com/2012/11/nosql-benchmark-redis-mongodb-ravendb-cassandra-sqlserver

https://github.com/redis/node-redis