Khi bạn đang thực hiện một dự án trẻ, bạn liên tục đưa ra các quyết định khiến việc mở rộng quy mô sau này trở nên dễ dàng hơn hoặc khó hơn. Đôi khi, bạn nên chọn khoản lợi nhuận ngắn hạn, tích lũy một chút nợ kỹ thuật để có thể giao hàng nhanh hơn. Nhưng những lần khác, chúng tôi nhận nợ kỹ thuật vì chúng tôi không biết có giải pháp thay thế.
Tôi có thể nói điều này bởi vì tại Honeybadger, chúng tôi đã làm một số điều khiến cuộc sống của chúng tôi trở nên khó khăn hơn nhiều so với hiện tại. Nếu chúng ta hiểu một vài điểm chính thì việc mở rộng quy mô sẽ đỡ đau hơn nhiều.
Sử dụng UUID
Khi bạn nói "khóa chính", hầu hết chúng ta nghĩ đến một số tự động tăng dần. Điều này hoạt động tốt cho các hệ thống nhỏ, nhưng nó gây ra một vấn đề lớn khi bạn mở rộng quy mô.
Tại bất kỳ thời điểm nào chỉ có một máy chủ cơ sở dữ liệu có thể tạo khóa chính. Điều đó có nghĩa là tất cả ghi phải đi qua một máy chủ duy nhất. Đó là tin xấu nếu bạn muốn viết hàng nghìn lần mỗi giây.
Sử dụng UUID làm khóa chính sẽ khắc phục được vấn đề này. Nếu bạn không quen thuộc với chúng, UUID là số nhận dạng duy nhất trông giống như sau:123e4567-e89b-12d3-a456-426655440000
.
Đây là cách wikipedia mô tả chúng:
Khi được tạo theo các phương pháp tiêu chuẩn, UUID dành riêng cho các mục đích thực tế mà không yêu cầu cơ quan đăng ký trung tâm hoặc sự phối hợp giữa các bên tạo ra chúng. Xác suất mà một UUID sẽ bị trùng lặp không phải là 0, nhưng gần bằng 0 đến mức không đáng kể.
Do đó, bất kỳ ai cũng có thể tạo một UUID và sử dụng nó để xác định một thứ gì đó mà gần như chắc chắn rằng số nhận dạng không trùng lặp với một thứ đã được tạo để xác định một thứ khác và sẽ không bị trùng lặp trong tương lai. Do đó, thông tin gắn nhãn UUID của các bên độc lập sau này có thể được kết hợp thành một cơ sở dữ liệu duy nhất hoặc được truyền trên cùng một kênh mà không cần giải quyết xung đột giữa các số nhận dạng.
Khi bạn sử dụng UUID làm khóa chính, tất cả các lần ghi không còn phải thông qua một cơ sở dữ liệu duy nhất. Thay vào đó, bạn có thể trải chúng ra trên nhiều máy chủ.
Ngoài ra, chúng cung cấp cho bạn sự linh hoạt để thực hiện những việc như tạo id của bản ghi trước đó nó được lưu vào cơ sở dữ liệu. Điều này có thể hữu ích nếu bạn muốn gửi bản ghi đến bộ nhớ cache hoặc máy chủ tìm kiếm, nhưng không muốn đợi giao dịch cơ sở dữ liệu hoàn tất.
Việc kích hoạt UUID theo mặc định trong ứng dụng Rails rất dễ dàng. Chỉ cần chỉnh sửa tệp cấu hình:
# config/application.rb
config.active_record.primary_key = :uuid
Bạn có thể sử dụng UUID cho một bảng riêng lẻ có di chuyển Rails:
create_table :users, id: :uuid do |t|
t.string :name
end
Đó là một tùy chọn cấu hình đơn giản, nếu bạn kích hoạt nó khi bắt đầu phát triển, sẽ giúp bạn tránh được một thế giới rắc rối khi bạn cố gắng mở rộng quy mô. Như một phần thưởng bổ sung, nó sẽ khiến bot và các tác nhân xấu khó đoán các URL riêng tư của bạn hơn.
Bộ đếm và bộ đếm
Một khi bạn bắt đầu tìm kiếm, số đếm và quầy tính tiền có ở khắp mọi nơi. Ứng dụng email của bạn hiển thị số lượng email chưa đọc. Các blog có chân trang phân trang sử dụng tổng số bài viết để tính số trang.
Có hai vấn đề về tỷ lệ với bộ đếm:
- Các truy vấn cơ sở dữ liệu như
select count(*) from users
vốn dĩ rất chậm. Chúng thực sự lặp lại từng bản ghi trong tập bản ghi để tạo ra kết quả của chúng. Nếu bạn có một triệu bản ghi, sẽ mất một lúc. - Nỗ lực tăng tốc bộ đếm bằng cách sử dụng "bộ nhớ đệm bộ đếm" có hiệu quả, nhưng chúng hạn chế khả năng của bạn trong việc lan truyền ghi trên nhiều máy chủ cơ sở dữ liệu.
Giải pháp đơn giản nhất là tránh sử dụng quầy ở bất cứ nơi nào có thể. Điều này dễ dàng hơn nhiều khi bạn đang thực hiện thiết kế ban đầu.
Ví dụ:bạn có thể chọn trang theo phạm vi ngày thay vì theo số lượng. Bạn có thể chọn không hiển thị một thống kê hữu ích ở mức độ nhẹ mà sẽ rất khó để tạo ra sau này. Bạn có được ý tưởng.
Dữ liệu nhập kho &hết hạn
Có một giới hạn trên về số lượng dữ liệu bạn có thể lưu trữ trong một bảng postgres duy nhất dựa trên số lượng RAM, CPU và IO đĩa mà bạn có. Điều đó có nghĩa là sẽ có lúc bạn cần di chuyển dữ liệu cũ ra khỏi các bảng chính của mình.
Hãy xem một trường hợp đơn giản. Bạn muốn xóa các bản ghi hơn một năm tuổi trong cơ sở dữ liệu lớn vài gigabyte.
Nếu bạn chưa từng giải quyết vấn đề này trước khi bạn có thể bị cám dỗ để làm điều gì đó như sau:
MyRecords.where("created_at < ?", 1.year.ago).destroy
Vấn đề là truy vấn này sẽ mất vài ngày hoặc vài tuần để chạy. Cơ sở dữ liệu của bạn quá lớn.
Đây là một vấn đề đặc biệt nhức nhối, bởi vì bạn thường không nhận ra mình mắc phải nó cho đến khi quá muộn. Hầu như không ai nghĩ đến chiến lược thanh lọc dữ liệu khi công ty của họ còn non trẻ và cơ sở dữ liệu của họ có 1.000 bản ghi.
Nếu bạn quản lý để lập kế hoạch trước, có một giải pháp dễ dàng. Chỉ cần phân vùng các bảng của bạn. Thay vì ghi tất cả dữ liệu của bạn vào my_records
bạn ghi dữ liệu của tuần này vào my_records_1
và dữ liệu của tuần tới tới my_records_2
. Khi đã đến lúc xóa tuần trước, chỉ cần drop table my_records_1
. Không giống như xóa, truy vấn này hoàn thành rất nhanh.
Cũng có thể phân vùng theo các trường khác với ngày. Bất cứ điều gì có ý nghĩa cho trường hợp sử dụng của bạn.
Thậm chí còn có một tiện ích mở rộng postgres có tên pg_partman chăm sóc tất cả các chi tiết và cho phép bạn phân vùng cơ sở dữ liệu của mình mà không cần thay đổi một dòng mã của bạn. Hoặc, nếu bạn muốn quản lý phân vùng trong Ruby, có một loại đá quý tiện dụng được gọi là có thể phân vùng
Lời chia tay
Lần tới khi bạn thấy mình đang xây dựng một dự án từ đầu, tôi khuyên bạn nên dành một chút thời gian để suy nghĩ về việc mở rộng quy mô. Đừng ám ảnh về nó. Đừng mất nhiều ngày để lo lắng về việc sử dụng HAML hay ERB. Nhưng hãy tự hỏi bản thân xem có bất kỳ chiến thắng dễ dàng nào mà bạn có thể giành được chỉ bằng cách lập kế hoạch trước hay không.