Tôi đã xây dựng một hệ thống như thế này cho một ứng dụng khoảng 8 năm trước và tôi có thể chia sẻ một vài cách mà nó đã phát triển khi việc sử dụng ứng dụng đã phát triển.
Tôi đã bắt đầu bằng cách đăng nhập mọi thay đổi (chèn, cập nhật hoặc xóa) từ bất kỳ thiết bị nào vào bảng "lịch sử". Vì vậy, nếu, chẳng hạn, ai đó thay đổi số điện thoại của họ trong bảng "liên hệ", hệ thống sẽ chỉnh sửa trường contact.phone và cũng thêm bản ghi lịch sử với action = update, field = phone, record = [contact ID], giá trị = [số điện thoại mới]. Sau đó, bất cứ khi nào một thiết bị đồng bộ hóa, nó sẽ tải xuống các mục lịch sử kể từ lần đồng bộ hóa cuối cùng và áp dụng chúng vào cơ sở dữ liệu cục bộ của nó. Điều này nghe có vẻ như mô hình "nhân rộng giao dịch" được mô tả ở trên.
Một vấn đề là giữ ID duy nhất khi các mục có thể được tạo trên các thiết bị khác nhau. Tôi không biết về UUID khi tôi bắt đầu điều này, vì vậy tôi đã sử dụng ID tăng tự động và viết một số mã phức tạp chạy trên máy chủ trung tâm để kiểm tra ID mới được tải lên từ thiết bị, thay đổi chúng thành ID duy nhất nếu có xung đột và báo cho thiết bị nguồn thay đổi ID trong cơ sở dữ liệu cục bộ của nó. Chỉ thay đổi ID của các bản ghi mới là không tệ, nhưng nếu tôi tạo, ví dụ: một mục mới trong bảng liên hệ, sau đó tạo một mục liên quan mới trong bảng sự kiện, bây giờ tôi có các khóa ngoại mà tôi cũng cần kiểm tra và cập nhật.
Cuối cùng, tôi đã học được rằng UUID có thể tránh được điều này, nhưng sau đó cơ sở dữ liệu của tôi đã trở nên khá lớn và tôi sợ việc triển khai UUID đầy đủ sẽ tạo ra vấn đề về hiệu năng. Vì vậy, thay vì sử dụng UUID đầy đủ, tôi bắt đầu sử dụng các khóa chữ và số 8 ký tự được tạo ngẫu nhiên làm ID và tôi để mã hiện tại của mình để xử lý xung đột. Ở đâu đó giữa các khóa 8 ký tự hiện tại của tôi và 36 ký tự của UUID phải có một điểm ngọt ngào để loại bỏ xung đột mà không cần phình to, nhưng vì tôi đã có mã giải quyết xung đột, nên không nên ưu tiên thử nghiệm điều đó .
Vấn đề tiếp theo là bảng lịch sử lớn hơn khoảng 10 lần so với toàn bộ cơ sở dữ liệu còn lại. Điều này làm cho việc lưu trữ trở nên đắt đỏ và bất kỳ bảo trì nào trên bảng lịch sử đều có thể gây đau đớn. Giữ toàn bộ bảng cho phép người dùng khôi phục mọi thay đổi trước đó, nhưng điều đó bắt đầu cảm thấy như quá mức cần thiết. Vì vậy, tôi đã thêm một thói quen vào quy trình đồng bộ hóa trong đó nếu mục lịch sử mà thiết bị được tải xuống lần cuối không còn tồn tại trong bảng lịch sử, thì máy chủ không cung cấp cho nó các mục lịch sử gần đây, mà thay vào đó cung cấp cho nó một tệp chứa tất cả dữ liệu cho tài khoản đó. Sau đó, tôi đã thêm một cronjob để xóa các mục lịch sử cũ hơn 90 ngày. Điều này có nghĩa là người dùng vẫn có thể khôi phục các thay đổi dưới 90 ngày và nếu họ đồng bộ hóa ít nhất một lần sau mỗi 90 ngày, các bản cập nhật sẽ được tăng dần như trước đây. Nhưng nếu họ chờ lâu hơn 90 ngày,
Sự thay đổi đó đã làm giảm gần 90% kích thước của bảng lịch sử, do đó, việc duy trì bảng lịch sử chỉ làm cho cơ sở dữ liệu lớn gấp đôi thay vì lớn gấp mười lần. Một lợi ích khác của hệ thống này là đồng bộ hóa vẫn có thể hoạt động mà không cần bảng lịch sử nếu cần - như nếu tôi cần thực hiện một số bảo trì tạm thời ngoại tuyến. Hoặc tôi có thể cung cấp các khoảng thời gian rollback khác nhau cho các tài khoản ở các mức giá khác nhau. Và nếu có hơn 90 ngày thay đổi để tải xuống, tệp hoàn chỉnh thường hiệu quả hơn định dạng gia tăng.
Nếu tôi đã bắt đầu lại từ hôm nay, tôi sẽ bỏ qua việc kiểm tra xung đột ID và chỉ nhắm đến độ dài khóa đủ để loại bỏ xung đột, với một số loại kiểm tra lỗi chỉ trong trường hợp. Nhưng bảng lịch sử và sự kết hợp của tải xuống gia tăng cho các bản cập nhật gần đây hoặc tải xuống đầy đủ khi cần đã hoạt động tốt.