BTC
ETH
HTX
SOL
BNB
Xem thị trường
简中
繁中
English
日本語
한국어
ภาษาไทย
Tiếng Việt

Một bài viết để hiểu cơ chế tăng tốc ảnh chụp nhanh ứng dụng khách Geth

以太坊爱好者
特邀专栏作者
Bài viết này có khoảng 4547 từ, đọc toàn bộ bài viết mất khoảng 7 phút
Cấu trúc tăng tốc ảnh chụp nhanh của Geth làm giảm độ phức tạp của việc đọc trạng thái theo một mức độ lớn.
Tóm tắt AI
Cấu trúc tăng tốc ảnh chụp nhanh của Geth làm giảm độ phức tạp của việc đọc trạng thái theo một mức độ lớn.

Lưu ý của biên tập viên: Bài viết này đến từNhững người đam mê Ethereum (ID: ethfans)Lưu ý của biên tập viên: Bài viết này đến từ

Những người đam mê Ethereum (ID: ethfans)

Những người đam mê Ethereum (ID: ethfans)

, Tác giả: Péter Szilágyi, bản dịch: A Jian, được ủy quyền in lại bởi Odaily.

Bài viết này là bài đầu tiên trong series Geth Client Answers, bạn có thể chủ động đặt câu hỏi về Geth client, mỗi tuần mình sẽ dành một bài viết nhỏ để trả lời những câu hỏi được bình chọn nhiều nhất. Câu hỏi được yêu cầu nhiều nhất trong tuần này là: Bạn có thể nói về sự khác biệt chính giữa cấu trúc cơ sở dữ liệu phẳng và cấu trúc kế thừa không?

Trạng thái Ethereum

Trước khi đi sâu vào cấu trúc tăng tốc (acceleration structure), chúng ta hãy xem lại khái niệm “trạng thái” trong Ethereum và cách trạng thái được lưu trữ khi chuyển sang các mức trừu tượng khác nhau.

Ethereum có hai loại trạng thái khác nhau: một tập hợp các tài khoản; và một tập hợp các vị trí tài khoản trên mỗi hợp đồng. Từ một quan điểm hoàn toàn trừu tượng, cả hai loại dữ liệu đều là các cặp khóa-giá trị. Bộ sưu tập tài khoản ánh xạ một địa chỉ tới số nonce, số dư, v.v. của địa chỉ đó. Trường lưu trữ của hợp đồng ánh xạ bất kỳ giá trị nào (được xác định và sử dụng bởi hợp đồng) với một số giá trị.

Thật không may, trong khi lưu trữ các cặp khóa-giá trị này dưới dạng dữ liệu phẳng có thể rất hiệu quả, việc xác minh tính chính xác của chúng trở nên khó khăn về mặt tính toán. Bất cứ khi nào dữ liệu được sửa đổi, chúng tôi phải băm tất cả dữ liệu từ dưới lên.

Để tránh phải luôn băm toàn bộ cơ sở dữ liệu, chúng ta có thể chia cơ sở dữ liệu thành các phần liền kề và tạo một loại cấu trúc cây! Dữ liệu gốc và hữu ích nhất được đặt trên các nút lá và mỗi nút bên trong trên cây là giá trị băm của nội dung bên dưới nút. Theo cách này, khi chúng ta muốn sửa đổi một số giá trị, chúng ta chỉ cần thực hiện số phép toán băm logarit. Loại cấu trúc dữ liệu này thực sự có một cái tên nổi tiếng, đó là "cây Merkel".

Nhưng vẫn chưa hết, phương pháp này vẫn còn thiếu độ phức tạp trong tính toán. Mặc dù cấu trúc cây Merkle rất hiệu quả khi sửa đổi dữ liệu hiện có, nhưng nếu việc chèn và xóa dữ liệu làm thay đổi ranh giới của các khối dữ liệu nhỏ bên dưới, nó sẽ làm mất hiệu lực tất cả các giá trị băm được tính toán.

Lúc này, thay vì nhóm cơ sở dữ liệu một cách mù quáng, chúng ta có thể sử dụng chính các khóa để sắp xếp dữ liệu và sắp xếp dữ liệu thành dạng cây dựa trên các tiền tố phổ biến! Theo cách này, cả thao tác chèn và xóa sẽ không ảnh hưởng đến tất cả các nút mà chỉ ảnh hưởng đến các nút (logarit) trên đường dẫn từ gốc của cây đến các lá. Cấu trúc dữ liệu này được gọi là "cây Patricia".

Kết hợp hai phương pháp trên - hệ thống phân cấp cây của cây Patricia và thuật toán băm của cây Merkle - được gọi là "cây Merkel-Patricia", cũng được sử dụng trong thực tế Một cấu trúc dữ liệu đại diện cho trạng thái của Ethereum. Cho dù đó là sửa đổi, chèn, xóa hay xác minh, chỉ có độ phức tạp logarit! Ngoại lệ nhỏ duy nhất là một số khóa được băm trước khi chèn để cân bằng số lần thử (Một điểm bổ sung nhỏ là các khóa được băm trước khi chèn để cân bằng số lần thử).

Lưu trữ trạng thái của Ethereum

Phần trên giải thích tại sao Ethereum sử dụng cấu trúc cây Merkle Patricia để lưu trữ trạng thái của nó. Thật không may, trong khi các thao tác cần thiết diễn ra nhanh chóng, thì mỗi tùy chọn đều phải hy sinh. Độ phức tạp logarit của các hoạt động cập nhật và xác minh có nghĩa là các lần đọc và lưu trữ cho từng khóa riêng lẻ là logarit (lần đọc logarit và lưu trữ logarit). Điều này là do mỗi nút bên trong của cấu trúc cây phải được lưu trữ riêng trên đĩa cứng.

Tại thời điểm này, tôi không biết chính xác độ sâu của cây tài khoản, nhưng khoảng một năm trước, trạng thái tài khoản đã lấp đầy cây cấp 7. Điều này có nghĩa là mọi thao tác trên cây (ví dụ: đọc số dư, ghi nonce) chạm vào ít nhất 7-8 nút bên trong và do đó thực hiện ít nhất 7-8 lần truy cập cơ sở dữ liệu liên tục. LevelDB tổ chức dữ liệu lên đến 7 lớp, do đó, có một hệ số nhân bổ sung. Kết quả cuối cùng là một lần truy cập trạng thái duy nhất dự kiến ​​sẽ khuếch đại thành 25-50 lần truy cập đĩa ngẫu nhiên. Bạn nhân số đó với tất cả trạng thái đọc và ghi của tất cả các giao dịch trong một khối và bạn sẽ nhận được một con số đáng sợ.

[Tất nhiên, tất cả các triển khai ứng dụng khách đều cố gắng giữ chi phí thấp nhất có thể. Geth sử dụng vùng bộ nhớ lớn hơn để lưu trữ một số nút; nó cũng sử dụng cơ chế cắt tỉa trong bộ nhớ để tránh ghi dữ liệu vào đĩa sẽ bị xóa sau một vài khối. Nhưng điều này cần một bài viết khác để làm rõ. ]

Điều đáng sợ nữa là con số này là chi phí chạy một nút Ethereum được đảm bảo để xác minh tất cả các trạng thái 24/7.

Chúng ta có thể làm tốt hơn không?

Không phải tất cả lượt truy cập đều được tạo như nhau

  • Hoạt động của Ethereum dựa trên bằng chứng mật mã về trạng thái. Miễn là chúng tôi muốn duy trì khả năng xác minh tất cả dữ liệu, chúng tôi không thể giải quyết vấn đề khuếch đại đọc và ghi đĩa cứng. Nghĩa là, chúng tôi — có thể và làm — tin tưởng vào dữ liệu mà chúng tôi đã xác minh.

  • Việc xác minh liên tục từng đối tượng trạng thái là vô nghĩa, nhưng nếu bạn phải xác minh nó mỗi khi bạn lấy dữ liệu từ đĩa cứng, thì bạn đang làm một việc vô nghĩa. Cấu trúc cây Merkle Patricia vốn được thiết kế cho thao tác ghi, nhưng lại trở thành gánh nặng cho thao tác đọc. Chúng ta không thể loại bỏ nó và không thể làm cho nó mỏng đi, nhưng điều đó không có nghĩa là chúng ta phải sử dụng nó trong mọi trường hợp.

  • Các kịch bản về trạng thái truy cập nút Ethereum có thể được tạm chia thành ba loại sau:

Khi nhập một khối mới, việc thực thi mã EVM sẽ tạo ra số lần đọc và ghi trạng thái ít nhiều cân bằng. Tuy nhiên, một khối được sử dụng cho các cuộc tấn công từ chối dịch vụ có thể tạo ra nhiều thao tác đọc hơn là thao tác ghi.

Khi toán tử nút truy xuất trạng thái (chẳng hạn như gọi ethcall và các thao tác tương tự), việc thực thi mã EVM chỉ tạo ra các thao tác đọc (tất nhiên có thể có các thao tác ghi, nhưng dữ liệu do các thao tác này tạo ra cuối cùng sẽ bị loại bỏ và sẽ không được duy trì vào bên trong ổ cứng).

Khi một nút đang đồng bộ hóa chuỗi khối, bộ đồng bộ hóa sẽ yêu cầu trạng thái từ nút từ xa và người được yêu cầu sẽ khai thác dữ liệu và truyền nó đến bộ đồng bộ hóa thông qua mạng.

Dựa trên các mẫu truy cập ở trên, nhiều thao tác nút có thể được thực hiện nhanh hơn nhiều nếu chúng ta có thể đoản mạch các thao tác đọc mà không cần chạm vào cây trạng thái. Điều này thậm chí có thể mở ra một số mẫu truy cập mới (chẳng hạn như lặp lại trạng thái) mà trước đây quá đắt để có thể thực hiện được.

Tất nhiên, hy sinh là điều không thể tránh khỏi. Nếu không loại bỏ cấu trúc cây, bất kỳ cấu trúc tăng tốc mới nào cũng sẽ giới thiệu thêm chi phí hoạt động. Câu hỏi duy nhất là: Liệu chi phí bổ sung có đủ để đảm bảo một lần thử không?

hãy làm theo nó

Chúng tôi đã phát triển cấu trúc cây Merkle Patricia kỳ diệu để giải quyết tất cả các vấn đề của chúng tôi và bây giờ chúng tôi muốn thực hiện các hoạt động đọc bỏ qua nó. Vì vậy, loại cấu trúc tăng tốc nào chúng ta nên sử dụng để làm cho hoạt động đọc trở lại nhanh hơn? Rõ ràng, nếu chúng ta không cần cấu trúc cây, thì chúng ta có thể ném tất cả sự phức tạp đi kèm với cấu trúc cây sang một bên và chúng ta có thể quay lại trạng thái ban đầu.

Như đã đề cập ở đầu bài viết này, về lý thuyết, phương thức lưu trữ dữ liệu của trạng thái Ethereum ở trạng thái lý tưởng phải là một cặp khóa-giá trị đơn giản. Để hiện thực hóa kế hoạch lý tưởng này!

Cách đây không lâu, Geth đã giới thiệu cấu trúc tăng tốc ảnh chụp nhanh (không được bật theo mặc định). Ảnh chụp nhanh là một cái nhìn đầy đủ về trạng thái của Ethereum tại một khối nhất định. Tóm tắt các chi tiết triển khai, nó chỉ đơn giản là sắp xếp tất cả các vị trí lưu trữ tài khoản và hợp đồng lại với nhau, được biểu thị bằng các cặp khóa-giá trị cố định.

Mỗi khi muốn truy cập vào một tài khoản hoặc một slot lưu trữ nào đó, chúng ta chỉ cần trả một thao tác truy vấn của LevelDB thay vì truy vấn 7-8 lần trên mỗi cây. Về lý thuyết, việc cập nhật snapshot cũng rất đơn giản, sau khi xử lý 1 block, chúng ta chỉ cần thực hiện thêm 1 thao tác ghi LevelDB cho mỗi slot lưu trữ được cập nhật.

Cấu trúc tăng tốc ảnh chụp nhanh thực sự làm giảm độ phức tạp của thao tác đọc từ O(log n) xuống O(1) (nhân với chi phí hoạt động của LevelDB), với chi phí thay đổi độ phức tạp của thao tác ghi từ O(log n) thành O( 1 + log n) (nhân với chi phí quản lý LevelDB) và tăng dung lượng lưu trữ đĩa từ O(n log n) lên O(n + n log n).

Ma quỷ là trong các chi tiết

Việc duy trì tính khả dụng của các ảnh chụp nhanh trạng thái Ethereum cũng không dễ dàng. Miễn là các khối vẫn đang được sản xuất nối tiếp nhau, xếp chồng lên khối cuối cùng, cách thô sơ để hợp nhất các thay đổi mới nhất vào ảnh chụp nhanh sẽ hoạt động tốt. Tuy nhiên, ngay cả với một tổ chức lại chuỗi khối nhỏ (thậm chí chỉ một khối), cơ chế chụp nhanh bị hỏng do các hoạt động hoàn tác hoàn toàn không được thiết kế. Đối với các mẫu biểu diễn dữ liệu phẳng, ghi liên tục là hoạt động một chiều. Và để làm cho vấn đề tồi tệ hơn, chúng tôi không có cách nào để truy cập các trạng thái cũ hơn (ví dụ: một số dApp cần truy cập trạng thái 3 khối trước; hoặc trạng thái 64 khối ở chế độ đồng bộ hóa nhanh/chụp nhanh).

Để khắc phục những hạn chế này, ảnh chụp nhanh máy khách Geth bao gồm hai phần: lớp đĩa cứng liên tục, là ảnh chụp nhanh hoàn chỉnh về trạng thái của khối cũ (ví dụ: 128 khối trên cùng); và lớp khác biệt trong bộ nhớ A cây thành phần được sử dụng để thu thập các thao tác ghi gần đây nhất.

  • Khi xử lý các khối mới, chúng tôi không hợp nhất các ghi này trực tiếp vào lớp đĩa cứng mà chỉ tạo một lớp khác biệt trong bộ nhớ mới chứa các thay đổi này. Khi các lớp khác bên trong bộ nhớ tích lũy đủ số lớp, lớp dưới cùng bắt đầu hợp nhất các bản cập nhật và đẩy chúng vào lớp đĩa cứng. Khi cần đọc một đối tượng trạng thái, chúng ta bắt đầu tìm kiếm từ lớp khác biệt trên cùng và đi xuống cho đến khi tìm thấy nó trong lớp khác biệt hoặc trong lớp đĩa cứng.

  • Biểu diễn dữ liệu này rất mạnh và giải quyết được nhiều vấn đề. Bởi vì lớp khác biệt bên trong bộ nhớ tạo thành một cây, nên việc tổ chức lại chuỗi trong 128 khối chỉ cần loại bỏ lớp khác biệt thuộc khối cha mẹ, sau đó bắt đầu xây dựng. dApps và farsyncers yêu cầu các trạng thái cũ hơn có thể truy cập 128 trạng thái gần đây nhất. Chi phí hoạt động trở thành 128 lần tra cứu ánh xạ, nhưng 128 lần tra cứu trong bộ nhớ nhanh hơn vài bậc so với 8 lần đọc đĩa cứng và mức khuếch đại DB cấp 4 đến 5 lần.

  • Tất nhiên, vẫn còn nhiều cạm bẫy. Tôi sẽ không đi sâu vào quá nhiều, mà chỉ đơn giản là liệt kê danh sách sau:

  • Tự hủy (các thao tác tự hủy theo hợp đồng) (và thao tác xóa) đặc biệt khó xử lý vì chúng yêu cầu đoản mạch phần gốc của lớp khác biệt.

  • Nếu có sự sắp xếp lại chuỗi sâu hơn lớp đĩa cứng liên tục, ảnh chụp nhanh hiện tại sẽ bị loại bỏ hoàn toàn và được tạo lại. Toàn bộ các hoạt động là rất tốn kém.

  • Khi nút bị tắt, lớp khác biệt trong bộ nhớ cần được lưu vào nhật ký và được tải để sao lưu, nếu không ảnh chụp nhanh sẽ vô dụng sau khi khởi động lại.

  • Sử dụng lớp khác biệt thấp nhất làm bộ tích lũy chỉ được chuyển vào đĩa khi nó vượt quá mức sử dụng bộ nhớ nhất định. Điều này cho phép khấu trừ ghi vào cùng một vị trí trên các khối.

  • Bộ đệm đọc phải được phân bổ cho lớp đĩa cứng để đĩa cứng không bị hỏng nếu hợp đồng liên tục truy cập vào cùng một khe lưu trữ cũ.

Sử dụng các bộ lọc nở tích lũy trong lớp khác biệt trong bộ nhớ để nhanh chóng phát hiện xem các đối tượng trạng thái có khả năng tồn tại trong lớp khác biệt hay nên chuyển trực tiếp vào đĩa.

Dữ liệu gốc (địa chỉ tài khoản, khóa lưu trữ hợp đồng) không được đặt làm khóa, nhưng giá trị băm của những dữ liệu này được sử dụng làm khóa để đảm bảo rằng thứ tự lặp lại của ảnh chụp nhanh giống với thứ tự của cây Merkle Patricia.

Thời gian để tạo lớp đĩa cứng liên tục dài hơn nhiều so với thời gian để cắt bớt cửa sổ cây trạng thái, do đó, ngay cả trình tạo cũng cần theo dõi động hoạt động của chuỗi.

Cái đẹp và cái xấu cùng tồn tại

Cấu trúc tăng tốc ảnh chụp nhanh của Geth làm giảm độ phức tạp của việc đọc trạng thái theo một mức độ lớn. Điều này có nghĩa là các cuộc tấn công DoS dựa trên dữ liệu đọc khó khởi chạy hơn và các cuộc gọi ethcall cũng nhanh hơn rất nhiều (giả sử không có tắc nghẽn CPU).

Tất nhiên, tất cả điều này không phải là không có chi phí. Sau khi quá trình đồng bộ hóa ban đầu hoàn tất, các nút tham gia vào mạng chính cần 9 đến 10 giờ để tạo ảnh chụp nhanh ban đầu (và sau đó duy trì tính khả dụng của nó) và cần có thêm ổ cứng hơn 15 GB.

phần kết

Phần xấu ở đâu? Chúng tôi đã mất 6 tháng để xây dựng đủ tự tin để phát hành cơ chế chụp nhanh và đây vẫn không phải là tính năng mặc định, nó cần được kích hoạt tích cực bằng cờ --snapshot và vẫn còn một số công việc hoàn thiện xung quanh việc sử dụng bộ nhớ và khôi phục sự cố làm.

Nói chung, chúng tôi rất tự hào về sự cải tiến này. Có một khối lượng công việc khổng lồ liên quan, và nó đang mò mẫm trong bóng tối, tự mình thực hiện mọi thứ và cầu nguyện rằng nó sẽ thành công. Một điều thú vị khác là phiên bản đầu tiên của đồng bộ hóa lá đã được viết cách đây hai năm rưỡi, nhưng nó đã bị chặn vì chúng tôi thiếu cấu trúc tăng tốc cần thiết để điều khiển nó.

开发者
ETH
Chào mừng tham gia cộng đồng chính thức của Odaily
Nhóm đăng ký:
https://t.me/Odaily_News
Nhóm trò chuyện:
https://t.me/Odaily_GoldenApe
Tài khoản chính thức:
https://twitter.com/OdailyChina
Mục lục bài viết
Thư viện tác giả
Tải ứng dụng Odaily Nhật Báo Hành Tinh
Hãy để một số người hiểu Web3.0 trước
IOS
Android