Check trùng cho nhiều quan hệ Rails + Mysql

Đăng bởi Lưu Đại vào ngày 18-12-2022

Vấn đề. 

Mình có 1 bảng A khoảng 50 cột khách hàng yêu cầu phải check trùng cho 25 cột trong bảng đó. Mỗi tổ hợp giá trị của 25 cột đó sẽ là duy nhất. Hệ quản trị cơ sở dữ liệu đang sử dụng là MySQL chỉ cho phép đánh index cho tối đa là 16 cột. Nếu check trùng bằng code thì mỗi lần lấy bản ghi mới ra sẽ phải duyệt hết tất cả các bản ghi hiện có trong db để check trùng ⇒ cách này không thể dùng được. 

Giải pháp

Mình tạo thêm 1 cột mới để lưu một mã hash unique Ban đầu mình lấy ra các giá trị của 25 cột cần check trùng cộng chuỗi giá trị này lại với nhau (có thể lưu luôn chuỗi này vào cột mới tuy nhiên trong trường hợp của mình chuỗi giá trị sau khi cộng có thể > 255 ký tự). Sau đó mình hash chuỗi giá trị cộng được ở bước trước do số lượng bản ghi không lớn nên mình sử dụng hash MD5. Sử dụng mã hash vẫn có tỉ lệ rất nhỏ 2 chuỗi giá trị khác nhau cho cùng mã hash với mã hash MD5 tỉ lệ này là 1.47*10-29. Bởi vì tỉ lệ trùng sẽ tăng tỉ lệ thuận với số lượng bản ghi nên trong trường hợp số lượng bản ghi nhiều có thể cân nhắc sử dụng hash mạnh hơn như hash 265, … Sau đó mình đánh index cho cột hash này trong db và validate trong models luôn =))) 

Note

  • Để làm cách này bạn phải đảm bảo đầu vào được sắp xếp cẩn thận vì nếu 2 giá trị giống nhau lệch vị trí sẽ cho ra 2 mã hash khác nhau. VD: col1: 1, col2: 2, col3: 3, col4: 4 ===Hash==⇒ hash1 col1: 1, col3: 3, col2: 2, col4: 4 ===Hash==⇒ hash2 Theo lý thuyết 2 bản ghi này trùng nhau tuy nhiên 2 mã hash nó tạo ra lại khác nhau. 
  • Chú ý thứ 2 là nếu bạn hash chuỗi params phức tạp ( về sau dự án mình làm tách bảng A ra thành 2 bảng riêng B và C. B là bảng master quan hệ 1 - N với C (chứa giá trị của 20 trong 25 cột) ) nên mình stringify object (chứa giá trị của cả các cột trong bảng A và B) rồi hash. Thì phải chú ý sort lần lượt từ trong ra ngoài các lớp nested (nếu có) cẩn thận format định dạng dữ liệu truyền lên nếu cần trước khi stringify vì một sai khác nhỏ thôi cũng đã cho ra 2 mã hash hoàn toàn khác nhau rồi.