Các điều học được sau khi được review code Redis

Nguồn:Redis 本番障害から学んだコードレビューの勘所 http://qiita.com/haminiku/items/43bafbb9d74ef3a1f74c

Trong quá trình phát triển nếu sử dụng Redis không đúng cách thì khi đi vào vận hành thực tế sẽ phát sinh rất nhiều vấn đề lớn nên cần tránh việc này ngay từ bước review code.

Redis tương thích tốt với ngôn ngữ script, nếu sử dụng thích hợp thì có thể tạo ra các chương trình tốc độ cao đáng ngạc nhiên khi so sánh với RDB. Bài viết này tổng hợp lại các tri thức xương máu học được từ các review comment tương đương khoảng 100 cuốn sách của đàn anh.

Điểm mấu chốt là với dữ liệu xóa được thì nên dùng Redis.

Khi bộ nhớ của Redis bị tràn

(Đây là một câu chuyện hư cấu)

Tôi bị đánh thức bởi một cuộc điện thoại nửa đêm. Có báo cáo lỗi không thể truy cập vào hệ thống. Một số thành viên công ty check thực tế thì không thể chơi được game của trang. Khả năng lớn là có vấn đề về dữ liệu nên trang đã được chuyển sang trạng thái đang bảo trì.

Nguyên nhân của sự cố được xác định là do Redis đã tiêu tốn hết bộ nhớ 😦

Dung lượng memory của server đã là 64GByte và không thể tăng thêm nữa nên việc cần thiết là phải giảm dung lượng memory của Redis.

RedisDB quá lớn không thể thực hiện profile tốt

Nếu ở trên môi trường local, có thể dùng các tool như rdm sử dụng cấu trúc cây để xác định khu vực có nhiều key nhưng với server Redis khoảng chừng 64Gb memory thì không thể dùng cấu trúc cây để nhìn tổng thể và xác định Key là nguyên nhân. Vì thế chỉ còn cách đọc code và tìm hiểu nguyên nhân.

Tuyệt vọng vì việc phân vùng dọc Redis tốn quá nhiều thời gian

Chúng tôi đã cố gắng thực hiện profile và tìm ra được nguyên nhân. Chúng tôi đã lưu những dữ liệu quan trọng của user trên Redis và không thể xóa chúng đi. Vì thế không có cách nào khác chúng tôi buộc phải dùng thêm một Redis server, copy một nửa số key sang. Việc viết batch và chạy mất 2 tiếng. Tuy nhiên khi kiểm tra Redis server mới thì mới chỉ có 5% cố lượng key được copy tức là cần khoảng 38h để kết thúc việc chuyển dữ liệu. Vì không thể dừng môi trường production trong 2 ngày nên bắt buộc phải cải thiện batch để copy dữ liệu.

Các nguyên nhân thường gây sập Redis server

Redis server bị down hay APP server bị down tùy theo tình hình:

  1. Từ các lệnh KEYS hoặc ZRANGE truy cập hàng chục nghìn record trở lên và server bị down do chờ I/O
  2. Do thiết kế data lỗi dẫn đến thiếu bộ nhớ và không thể ghi thêm nên server down

Các cách khắc phục cơ bản khi Redis server gặp lỗi

Đầu tiên cần tìm hiểu nguyên nhân là do thiếu memory hay do chờ I/O

  1. Sửa các lệnh ZRANGE hoặc keys * chạy tốn thời gian
  2. Xóa các Key không cần thiết
  3. Thêm server, thực hiện phân vùng dọc.
  4. Thay đổi thiết kế data, chuyển nơi lưu trữ data từ Redis sang RDB

Quan điểm review code

Thực hiện 3 quan điểm review code sau:

  • Tránh việc suy giảm I/O của Redis server
  • Tránh việc memory bị dùng quá nhiều
  • Đảm bảo tính toàn vẹn của dữ liệu

1. Cẩn thận khi dùng Redis

Nếu một service có số lượng người sử dụng lớn thì chỉ một lỗi thiết kế dữ liệu cũng có thể khiến cho Redis server hoặc APP server ngừng hoạt động. Khi implement sử dụng Redis, cần nghĩ đến tình huống xấu nhất và lúc review cần hết sức thận trọng.

2. Cần đưa ra comment ngay khi coder không sử dụng Cache mà lưu trữ Storage bình thường

Các dữ liệu quan trọng cần lưu ở RDB, chỉ lưu Cache ở Redis. Nguyên tắc cơ bản là chỉ lưu ở Redis các dữ liệu có thể xóa được. Nếu lưu các dữ liệu quan trọng ở Redis, thì khi cần rollback transaction của DB sẽ không thể rollback dữ liệu ở Redis và sẽ đẫn đến dữ liệu mất tính toàn vẹn.

3. Có đang set Expire không?

Trên Redis có thể set Expire(TTL) cho mỗi key. Nếu sử dụng cho Cache thì Expire có thể set dài nhất là khoảng 1 tháng. Nếu cần set Expire trên 1 tháng, về cơ bản có nghĩa là đã dùng Redis như Storage. Nên thay đổi thiết kế, sử dụng RDB.

4. Không sử dụng lệnh ZRANGEBYSCORE

SortedSet của Redis sử dụng realtime ranking nên rất tiện lợi với việc phát triển list cần thay đổi liên tục. Sử dụng có thể implement các chức năng mà không thể làm được trên RDB. Lệnh ZRANGEBYSCORE là lệnh xác định khoảng score từ SortedSet và lấy dữ liệu. Khi dữ liệu phân bố không đều, nếu trên 1 query cần lấy vài chục nghìn bảng ghi, thời gian đáp ứng quá 1s, do chờ I/O sẽ dẫn đến Redis server hoặc APP server down. Tất nhiên còn tùy trường hợp nhưng về cơ bản là không được. Trong trường hợp này cần dùng lệnh ZREVRANGE có thể xác định khoảng rank chứ không phải score Redis.png

5. Không dùng lệnh keys *

Lệnh keys là lệnh tự do trả về tất cả các KEY phù hợp. Cũng tương tự, nếu dùng lệnh này khi dữ liệu phân bố không đều, thời gian chờ I/O sẽ khiến server down. Nếu cần tìm kiếm đến mức phải dùng lệnh keys thì nên đưa dữ liệu vào RDB, đánh index và sử dụng câu lệnh WHERE.

6. Vẫn có trường hợp bắt buộc phải dùng Redis như một Storage

Chuyện gì cũng có ngoại lệ. Ví dụ khi không thể không dùng các chức năng đặc trưng của Redis như SortedSet. Khi đó sẽ review theo quan điểm dưới đây.

6-1. Có thiết kế xử lý được vấn đề không toàn vẹn dữ liệu khi rollback không?

Trong khi transaction DB nếu có vấn đề xảy ra cần rollback thì Redis không thể rollback. Khi xảy ra việc không đồng nhất dữ liệu giữa dữ liệu của Redis và RDB có thể rollback, cần phải có thiết kế để có thể back dữ liệu chuẩn. Có thể thực hiện bằng cách lưu lặp dữ liệu trên cả RDB và Redis, từ View truy cập vào Redis cho nhanh, khi update dữ liệu thì dùng dữ liệu chuẩn trên RDB, ghi đè lên Redis.

6-2 Khi dữ liệu của Redis bị xóa thì có phục hồi được không (Không cần thiết)

Khi xảy ra việc mấy toàn vẹn dữ liệu trên Redis, để có thể khôi phục được dữ liệu, cần phải chuẩn bị tất cả lệnh phụ hồi trước. Nếu có thể lưu trên RDB thì khi có sự cố xảy ra, cho dù viết code thì cũng không vấn đề gì nên không cần khắc phục.

Lệnh bigkeys rất tiện lợi với profile của Redis

bigkeys là lệnh khảo sát sample, dù với DB rất lớn thì trong một khoảng thời gian không quá dài có teher kết thúc khảo sát. Khảo sát 11 triệu KEY mất khoảng 1 phút. Ở môi trường production của dự án có nhiều người tham gia, có thể sẽ không biết được những KEY cũ nào có thể xóa được nhưng có thể dựa vào hint như sau. Vì thông tin trên internet rất ít nên đã dựa vào bigkeys của redis-cli.c Profile DB của dự án cá nhân có 11 triệu Key

>> redis-cli -n 11 --bigkeys

#Scanning the entire keyspace to find biggest keys as well as
#average sizes per key type.

You can use -i 0.1 to sleep 0.1 sec
per 100 SCAN commands (not usually needed).

[00.00%] Biggest list   found so far 'CF:INDEX:GOODS-HIS:CAMERA:563749' with 51 items
[00.00%] Biggest hash   found so far 'CF:GOODS:TAG:208737' with 1 fields
[00.00%] Biggest list   found so far 'CF:INDEX:GOODS-HIS:TAG7:554718' with 56 items
[00.00%] Biggest list   found so far 'CF:INDEX:GOODS-HIS:CAMERA:62917' with 65 items
[00.01%] Biggest list   found so far 'CF:INDEX:GOODS-HIS:TAG10:798984' with 66 items
[00.01%] Biggest list   found so far 'CF:INDEX:GOODS-HIS:DVD:585528' with 73 items
[00.06%] Biggest list   found so far 'CF:INDEX:GOODS-HIS:CLOTHES:739957' with 74 items
[00.44%] Biggest list   found so far 'CF:INDEX:GOODS-HIS:DVD:241918' with 83 items
[06.03%] Biggest list   found so far 'CF:INDEX:GOODS-HIS:CLOTHES:452765' with 87 items
[09.01%] Sampled 1000000 keys so far
[18.02%] Sampled 2000000 keys so far
[27.03%] Sampled 3000000 keys so far
[36.03%] Sampled 4000000 keys so far
[45.04%] Sampled 5000000 keys so far
[54.05%] Sampled 6000000 keys so far
[63.06%] Sampled 7000000 keys so far
[69.93%] Biggest list   found so far 'CF:INDEX:GOODS-HIS:COMPUTER:328426' with 89 items
[72.07%] Sampled 8000000 keys so far
[81.08%] Sampled 9000000 keys so far
[90.09%] Sampled 10000000 keys so far
[99.10%] Sampled 11000000 keys so far

-------- summary -------

Sampled 11100436 keys in the keyspace!
Total key length in bytes is 588720855 (avg len 53.04)

Biggest   list found 'CF:INDEX:GOODS-HIS:COMPUTER:328426' has 89 items
Biggest   hash found 'CF:GOODS:TAG:208737' has 1 fields

0 strings with 0 bytes (00.00% of keys, avg size 0.00)
10100437 lists with 100997852 items (90.99% of keys, avg size 10.00)
0 sets with 0 members (00.00% of keys, avg size 0.00)
999999 hashs with 999999 fields (09.01% of keys, avg size 1.00)
0 zsets with 0 members (00.00% of keys, avg size 0.00)