Why is single threaded Redis so fast

  sonic0002        2023-02-28 05:16:22       32,486        2          English  简体中文  Tiếng Việt 

Redis là một cơ sở dữ liệu khóa-giá trị trong bộ nhớ, hiệu năng cao. Theo các báo cáo thử nghiệm chính thức, nó có thể hỗ trợ khoảng 100.000 QPS (truy vấn mỗi giây) trên một máy đơn lẻ. Tuy nhiên, Redis sử dụng kiến trúc đơn luồng trong thiết kế của nó.

Tại sao Redis vẫn có hiệu năng cao như vậy với thiết kế đơn luồng? Sử dụng nhiều luồng để xử lý yêu cầu đồng thời sẽ tốt hơn không?

Trong bài viết này, chúng ta hãy cùng tìm hiểu lý do tại sao Redis có kiến trúc đơn luồng và vẫn duy trì tốc độ của nó. Trọng tâm là bốn khía cạnh sau:

  • Lưu trữ dữ liệu trong bộ nhớ
  • Cấu trúc dữ liệu hiệu quả
  • Kiến trúc đơn luồng
  • I/O không chặn

Hãy phân tích từng khía cạnh một cách chi tiết.

Lưu trữ dữ liệu trong bộ nhớ

Redis hoàn toàn dựa trên bộ nhớ, với dữ liệu được lưu trữ trong bộ nhớ. Phần lớn các yêu cầu là các hoạt động bộ nhớ thuần túy, cực kỳ nhanh chóng. So với việc lưu trữ dữ liệu tệp đĩa truyền thống, Redis tránh được chi phí đọc dữ liệu từ đĩa vào bộ nhớ thông qua I/O đĩa.

Cấu trúc dữ liệu hiệu quả

Redis có tổng cộng 5 kiểu dữ liệu: String, List, Hash, Set, và SortedSet.

Các kiểu dữ liệu khác nhau sử dụng một hoặc nhiều cấu trúc dữ liệu ở cấp độ thấp để hỗ trợ chúng, với mục tiêu đạt được tốc độ nhanh hơn.

Kiến trúc đơn luồng

Sử dụng một luồng duy nhất tiết kiệm rất nhiều thời gian về việc chuyển đổi ngữ cảnh và tiêu thụ CPU, không có điều kiện đua, không cần phải xem xét các vấn đề khóa khác nhau, và không có các hoạt động khóa và mở khóa có thể gây ra chi phí hiệu năng do bế tắc. Ngoài ra, nó cho phép sử dụng nhiều lệnh "không an toàn luồng", chẳng hạn như Lpush.

Lưu ý rằng khi chúng ta nhấn mạnh đơn luồng, chúng ta đang đề cập đến việc sử dụng một luồng để xử lý I/O mạng và đọc ghi cặp khóa-giá trị (bộ phân phối sự kiện tệp). Nói cách khác, một luồng xử lý tất cả các yêu cầu mạng, nhưng các chức năng khác của Redis, chẳng hạn như tính bền vững, xóa bỏ không đồng bộ và đồng bộ hóa dữ liệu cụm, thực tế được thực hiện bởi các luồng bổ sung.

Vậy tại sao lại sử dụng đơn luồng? Câu trả lời chính thức là vì CPU không phải là nút thắt của Redis, mà rất có thể là bộ nhớ máy hoặc băng thông mạng. Vì một luồng duy nhất dễ triển khai và CPU sẽ không trở thành nút thắt, nên việc áp dụng giải pháp đơn luồng là hợp lý.

Mặc dù kiến trúc đa luồng cho phép một ứng dụng xử lý các tác vụ đồng thời thông qua chuyển đổi ngữ cảnh, nhưng nó chỉ mang lại sự tăng cường hiệu năng nhỏ cho Redis vì hầu hết các luồng cuối cùng sẽ bị chặn bởi I/O mạng.

Điều quan trọng cần lưu ý là vì Redis sử dụng một luồng duy nhất, nếu một lệnh mất quá nhiều thời gian để thực thi (chẳng hạn như lệnh hgetall), nó có thể gây ra hiện tượng chặn. Redis là một cơ sở dữ liệu bộ nhớ được thiết kế để thực thi nhanh, vì vậy điều quan trọng là phải thận trọng khi sử dụng các lệnh như lrange, smembers, hgetall, v.v.

I/O không chặn

Sử dụng mô hình luồng dựa trên đa luồng I/O mạng (I/O không chặn) cho phép xử lý các kết nối đồng thời và giúp giảm bớt vấn đề tốc độ I/O mạng chậm.

Mô hình I/O đa luồng tận dụng khả năng của select, poll và epoll để đồng thời giám sát các sự kiện I/O của nhiều luồng. Khi nhàn rỗi, luồng hiện tại bị chặn. Khi một hoặc nhiều luồng có sự kiện I/O, luồng được đánh thức khỏi trạng thái bị chặn, và chương trình thăm dò tất cả các luồng (epoll chỉ thăm dò các luồng đã tạo ra sự kiện), sau đó xử lý tuần tự các luồng sẵn sàng. Phương pháp này tránh được một số lượng lớn các hoạt động không cần thiết.

Ở đây, "đa luồng" đề cập đến nhiều kết nối mạng, và "tái sử dụng" đề cập đến việc tái sử dụng cùng một luồng. Việc sử dụng công nghệ I/O đa luồng cho phép một luồng duy nhất xử lý hiệu quả nhiều yêu cầu kết nối I/O mạng của máy khách (để giảm thiểu thời gian dành cho I/O mạng).

Bộ xử lý sự kiện mạng của Redis dựa trên mô hình Reactor, còn được gọi là bộ xử lý sự kiện tệp.

Bộ xử lý sự kiện tệp sử dụng đa luồng I/O để đồng thời lắng nghe nhiều socket và liên kết các tác vụ được thực hiện bởi các socket với các bộ xử lý sự kiện khác nhau.

Sự kiện tệp chạy theo cách đơn luồng, nhưng bằng cách sử dụng các chương trình đa luồng I/O để lắng nghe nhiều socket, bộ xử lý sự kiện tệp thực hiện một mô hình giao tiếp mạng hiệu năng cao.

Redis xử lý các yêu cầu của máy khách, bao gồm nhận (đọc socket), phân tích cú pháp, thực thi và gửi (ghi socket) bằng một luồng chính tuần tự duy nhất, đó là mô hình đơn luồng được gọi.

Nhiều socket có thể tạo ra các hoạt động khác nhau, và mỗi hoạt động tương ứng với một sự kiện tệp khác nhau. Tuy nhiên, chương trình đa luồng I/O lắng nghe nhiều socket và xếp hàng các sự kiện do các socket tạo ra. Bộ phân phối sự kiện lấy một sự kiện từ hàng đợi mỗi lần và chuyển sự kiện đó đến bộ xử lý sự kiện tương ứng để xử lý.

Các cuộc gọi máy khách Redis đến máy chủ trải qua ba quy trình: gửi lệnh, thực thi lệnh và trả về kết quả. Trong giai đoạn thực thi lệnh, vì Redis xử lý lệnh theo cách đơn luồng, mỗi lệnh đến máy chủ không được thực thi ngay lập tức. Tất cả các lệnh được đưa vào hàng đợi và được thực thi lần lượt. Thứ tự thực thi các lệnh được gửi bởi nhiều máy khách không chắc chắn. Tuy nhiên, chắc chắn rằng hai lệnh sẽ không được thực thi đồng thời, tránh các vấn đề đồng thời. Đây là mô hình đơn luồng cơ bản của Redis.

Giải thích về đa luồng Redis 6.0

Tại sao đa luồng không được sử dụng trong Redis trước phiên bản 6.0?

Redis đã sử dụng phương pháp đơn luồng để đạt được khả năng bảo trì cao. Mặc dù đa luồng có thể hoạt động tốt ở một số khía cạnh, nhưng nó tạo ra sự không chắc chắn trong thứ tự thực thi chương trình, dẫn đến một loạt các vấn đề với việc đọc và ghi đồng thời. Điều này làm tăng độ phức tạp của hệ thống và có thể dẫn đến mất hiệu năng do chuyển đổi luồng, khóa và mở khóa, và thậm chí là bế tắc.

Tại sao Redis 6.0 lại giới thiệu đa luồng?

Redis 6.0 đã giới thiệu đa luồng vì nút thắt của nó không nằm ở bộ nhớ, mà nằm ở mô-đun I/O mạng, đã tiêu tốn thời gian CPU. Do đó, đa luồng được giới thiệu để xử lý I/O mạng và tận dụng tối đa tài nguyên CPU, giảm thiểu mất hiệu năng do I/O mạng bị chặn.

Làm thế nào để bật đa luồng trong Redis 6.0?

Theo mặc định, đa luồng bị tắt trong Redis, và nó có thể được bật trong tệp conf:

io-threads-do-reads yes 
io-threads [số luồng]

Số luồng được đề xuất theo hướng dẫn chính thức là đặt 2-3 luồng cho máy 4 nhân và 6 luồng cho máy 8 nhân. Số luồng nên nhỏ hơn số nhân của máy và không nên vượt quá 8 luồng nếu có thể.

Có bất kỳ vấn đề đồng thời luồng nào trong chế độ đa luồng không?

Như thể hiện trong sơ đồ, một yêu cầu Redis liên quan đến việc thiết lập kết nối, lấy lệnh để thực thi, thực thi lệnh và cuối cùng là ghi phản hồi vào socket.

Trong chế độ đa luồng của Redis, việc nhận, gửi và phân tích cú pháp lệnh có thể được cấu hình để thực thi trong nhiều luồng vì chúng là những điểm tiêu tốn thời gian chính mà chúng ta đã xác định. Tuy nhiên, việc thực thi lệnh, liên quan đến các hoạt động bộ nhớ, vẫn chạy trong một luồng duy nhất.

Do đó, phần đa luồng của Redis chỉ được sử dụng để xử lý việc đọc và ghi dữ liệu mạng và phân tích cú pháp giao thức. Việc thực thi lệnh vẫn được thực thi tuần tự trong một luồng duy nhất, vì vậy không có vấn đề về an toàn đồng thời.

Tham khảo: 单线程的 Redis 为什么这么快?

REDIS  SINGLE-THREADED  MULTI-THREADING 

       

  RELATED


  2 COMMENTS


Anonymous [Reply]@ 2024-10-17 20:55:48

????????

test1 [Reply]@ 2024-10-28 02:29:22

check the box???



  RANDOM FUN

Breakpoint while debugging