Ngày Tận Thế Mã Hóa: Khi UTF-8 Không Phải Là UTF-8
Tai nạn dữ liệu tồi tệ nhất mà tôi từng chứng kiến xảy ra tại một chuỗi bán lẻ đa quốc gia vào năm 2017. Họ đang hợp nhất dữ liệu khách hàng từ 47 cơ sở dữ liệu khu vực trên 12 quốc gia vào một kho dữ liệu duy nhất. Nghe có vẻ đơn giản, đúng không? Xuất sang CSV, nhập vào kho, chạy một số logic loại bỏ trùng lặp, và bạn xong. Ngoại trừ các tệp CSV từ bộ phận Pháp liên tục làm hỏng tên. "François" trở thành "François". "Chloé" trở thành "Chloé". Bộ phận Đức có các vấn đề tương tự với Umlaut. Dữ liệu của bộ phận Nhật Bản thì hoàn toàn không thể đọc được—chỉ là những hàng dấu hỏi và ký tự thay thế. Nguyên nhân gốc rễ? Mỗi nhóm khu vực đã xuất CSV của họ bằng các mã hóa khác nhau. Pháp sử dụng ISO-8859-1 (Latin-1). Đức sử dụng Windows-1252. Nhật Bản sử dụng Shift-JIS. Các nhóm Anh và Mỹ sử dụng UTF-8, nhưng một số sử dụng UTF-8 với BOM (Byte Order Mark) và một số thì không. Một nhóm ở Tây Ban Nha đã xuất dữ liệu của họ bằng UTF-16LE. Dự án hợp nhất, ban đầu được lập kế hoạch trong ba tháng, đã mất mười một tháng. Chúng tôi đã phải xây dựng một quy trình phát hiện mã hóa tùy chỉnh sẽ: 1. Cố gắng phát hiện mã hóa bằng cách sử dụng nhiều thư viện (chardet, charset-normalizer và một thuật toán tùy chỉnh) 2. Xác thực phát hiện bằng cách kiểm tra các mẫu ký tự thông dụng trong mỗi ngôn ngữ 3. Chuyển đổi mọi thứ sang UTF-8 mà không có BOM 4. Ghi lại mọi chuyển đổi với điểm số xác thực để kiểm tra thủ công Ngay cả với quy trình này, chúng tôi đã có tỷ lệ lỗi 3% cần sửa thủ công. Đó là 3% của 47 triệu hồ sơ khách hàng—1,4 triệu tên cần được người xem xét. Bài học? Đừng bao giờ tin tưởng vào mã hóa của CSV. Chưa bao giờ. Ngay cả khi ai đó nói với bạn "nó chắc chắn là UTF-8," hãy xác minh điều đó. Tôi đã thấy những tệp tuyên bố là UTF-8 trong siêu dữ liệu của chúng nhưng thực tế là Windows-1252 với các ký tự ASCII cao. Tôi đã thấy tệp UTF-8 với các đoạn ISO-8859-1 ngẫu nhiên nơi ai đó đã sao chép-dán từ một hệ thống cũ. Tôi thậm chí đã thấy một tệp đổi mã hóa giữa chừng vì tập lệnh xuất đã gặp sự cố và khởi động lại với các cài đặt vùng lãnh thổ khác nhau. Giờ đây, mỗi tệp CSV mà vượt qua bàn làm việc của tôi đều được chạy qua một tập lệnh xác thực mã hóa trước khi tôi thậm chí nhìn vào dữ liệu. Nó đã cứu tôi hàng giờ và ngăn ít nhất một chục sự cố lớn.Khoảng Trắng Không Tồn Tại (Ngoại Trừ Nó Có)
Vào năm 2018, tôi được mời vào để sửa một hệ thống đối chiếu tài chính đã bị hỏng trong sáu tháng. Công ty là một nhà xử lý thanh toán xử lý hàng tỷ đô la giao dịch. Quy trình đối chiếu của họ so sánh hồ sơ giao dịch từ cơ sở dữ liệu của họ với các báo cáo CSV từ các đối tác ngân hàng. Hệ thống đang báo cáo hàng nghìn sự không khớp mỗi ngày—các giao dịch xuất hiện trong báo cáo ngân hàng nhưng không có trong cơ sở dữ liệu của họ, hoặc ngược lại. Nhóm tài chính đang đối chiếu các sự không khớp này một cách thủ công, làm việc 60 giờ một tuần để theo kịp. Họ đã kiểm tra từng giao dịch bị đánh dấu và phát hiện rằng thực tế là chúng tồn tại trong cả hai hệ thống. Các mã giao dịch đã khớp hoàn hảo. Nhưng hệ thống tự động cứ liên tục đánh dấu chúng là không khớp. Tôi đã dành hai ngày phân tích mã, các truy vấn cơ sở dữ liệu, và logic phân tích CSV. Mọi thứ đều trông đúng. Rồi tôi đã làm điều gì đó mà lẽ ra nên rõ ràng từ đầu: tôi mở tệp CSV trong một trình chỉnh sửa hex. Và rồi nó xuất hiện. Mỗi mã giao dịch trong các tệp CSV của ngân hàng đều có một khoảng cách ở cuối. Không nhìn thấy trong Excel. Không nhìn thấy trong hầu hết các trình soạn thảo văn bản. Nhưng ở đó, trong bản sao hex: `54 52 41 4E 53 31 32 33 34 35 20` thay vì `54 52 41 4E 53 31 32 33 34 35`. Ký tự cuối cùng `20` là một ký tự khoảng trắng. Cơ sở dữ liệu đã lưu trữ các mã giao dịch mà không có khoảng trắng ở cuối. Logic so sánh đang thực hiện khớp chuỗi chính xác. "TRANS12345" ≠ "TRANS12345 ". Hàng nghìn sự không khớp sai, hàng trăm giờ bị lãng phí, tất cả chỉ vì một ký tự khoảng trắng ở cuối. Nhưng đây là chỗ tệ hơn: các khoảng trắng ở cuối không đồng nhất. Một số mã giao dịch có chúng, một số không. Một số có khoảng trắng ở cuối, một số có khoảng trắng ở đầu, và một số có cả hai. Vài tệp có tab thay vì khoảng trắng. Một tệp đáng nhớ đã có sự kết hợp của khoảng trắng, tab và khoảng trắng không ngắt (U+00A0). Cách sửa chữa rất đơn giản—cắt tất cả khoảng trắng trong quá trình nhập. Nhưng bài học thì sâu sắc: khoảng trắng trong các tệp CSV không bao giờ ngẫu nhiên, luôn luôn gây vấn đề, và thường thì không nhìn thấy. Giờ đây, tôi có một quy tắc: mọi trường chuỗi đều được cắt khi nhập, không có ngoại lệ. Tôi không quan tâm nếu logic kinh doanh nói rằng trường nên bảo tồn khoảng trắng. Tôi không quan tâm nếu ai đó khăng khăng rằng dữ liệu sạch. Cắt tất cả. Tôi cũng đã học được cách theo dõi các ký tự vô hình khác: các khoảng trắng chiều rộng bằng 0 (U+200B), các ký tự không nối chiều rộng bằng 0 (U+200C), các ký tự nối chiều rộng bằng 0 (U+200D), và ký tự đánh dấu byte order đáng sợ (U+FEFF) đôi khi xuất hiện ở giữa các tệp. Những ký tự này là những hồn ma trong máy, vô hình với con người nhưng rất thực đối với máy tính.Định Dạng Ngày Tháng Đã Phá Vỡ Thương Mại Quốc Tế
Hãy để tôi kể cho bạn về lần tôi gặp một định dạng ngày tháng bị nguyền rủa, bị hỏng một cách cơ bản, đến mức nó vẫn ám ảnh giấc mơ của tôi. Đây là tại một công ty logistics điều phối vận chuyển giữa các nhà sản xuất ở châu Á và các nhà bán lẻ ở Bắc Mỹ và châu Âu. Hệ thống hoạt động như sau: các nhà sản xuất sẽ tải lên các tệp CSV với chi tiết vận chuyển, bao gồm ngày lấy hàng, ngày dự kiến giao hàng, và ngày thông quan. Hệ thống sẽ phân tích các ngày này, tính toán thời gian vận chuyển, và phối hợp với các công ty vận chuyển và môi giới hải quan. Mọi thứ đã hoạt động tốt trong nhiều năm. Rồi, vào tháng 3 năm 2016, hệ thống bắt đầu lên lịch vận chuyển cho các ngày trong quá khứ. Các container đáng lẽ phải được lấy vào ngày 15 tháng 3 năm 2016, lại được lên lịch cho ngày 15 tháng 3 năm 1916. Giấy tờ hải quan đang được nộp cho các ngày trước khi việc vận chuyển container được phát minh. Nguyên nhân gốc rễ? Định dạng ngày tự động của Excel kết hợp với sự khác biệt về định dạng ngày tháng vùng và sự hiểu lầm thật sự kinh khủng về cách các ngày hoạt động. Đây là những gì đã xảy ra: 1. Một nhà sản xuất ở Trung Quốc sẽ nhập một ngày như "3/15/2016" (ngày 15 tháng 3 năm 2016 theo định dạng MM/DD/YYYY) 2. Excel sẽ hiểu điều này là một ngày và lưu trữ nó nội bộ dưới dạng một số sê-ri (42444 cho ngày 15 tháng 3 năm 2016) 3. Khi được xuất sang CSV, Excel sẽ định dạng nó theo vùng lãnh thổ hệ thống 4. Vùng lãnh thổ hệ thống Trung Quốc sử dụng định dạng YYYY-MM-DD, vì vậy nó đã được xuất dưới dạng "2016-03-15" 5. Hệ thống nhập của chúng tôi, được cấu hình cho định dạng MM/DD/YYYY, sẽ phân tích "2016-03-15" như "2016/03/15" (tháng 2016, ngày 3, năm 15) 6. Vì tháng 2016 không hợp lệ, trình phân tích sẽ quay lại để hiểu nó như "20/16/03/15" 7. Thông qua một loạt các nỗ lực phân tích ngày càng tuyệt vọng, nó cuối cùng sẽ định hình thành "03/15/1916" Nhưng khoan đã, còn tệ hơn. Một số nhà sản xuất đã sử dụng định dạng DD/MM/YYYY. Một số đã sử dụng YYYY-MM-DD. Một số đã sử dụng MM/DD/YY (năm hai chữ số). Và một nhà sản xuất ở Đài Loan đã sử dụng lịch Minguo, trong đó năm 105 tương ứng với năm 2016 CN (1912 + 105). Chúng tôi đã có những ngày trải dài từ 1916 đến 2116, với một cụm đặc biệt dày đặc xung quanh năm 1970 (thời đại Unix) bởi vì một số hệ thống đã xuất các ngày dưới dạng dấu thời gian Unix và trình phân tích của chúng tôi đã hiểu chúng như định dạng YYYYMMDD. Giải pháp yêu cầu: - Triển khai một trình phân tích ngày đa chiến lược sẽ cố gắng phát hiện định dạng - Thêm quy tắc xác thực (loại bỏ các ngày trước năm 2000 hoặc sau năm 2050) - Yêu cầu các nhà sản xuất sử dụng định dạng ISO 8601 (YYYY-MM-DD) độc quyền - Xây dựng một giao diện web cho việc tải lên CSV, sẽ xem trước các ngày đã được phân tích trước khi nhập - Tạo một bộ kiểm thử toàn diện với các ngày dưới mọi định dạng có thể Ngay cả với tất cả các biện pháp bảo vệ này, thi thoảng chúng tôi vẫn gặp lỗi phân tích ngày tháng. Chỉ trong tháng trước, tôi gặp một tệp CSV mà ai đó đã nhập "2/29/2023" (ngày 29 tháng 2 năm 2023—một ngày không tồn tại vì năm 2023 không phải là năm nhuận). Excel vui vẻ chấp nhận, lưu nó dưới dạng số sê-ri 45000, và xuất nó dưới dạng "2023-02-29". Hệ thống của chúng tôi đã nhập, xác minh rằng nó ở định dạng đúng, và đã lên lịch giao hàng cho một ngày không tồn tại."Vấn đề với các ngày tháng là mọi người đều nghĩ họ hiểu chúng, nhưng không ai thực sự hiểu. Ngày tháng là những cấu trúc văn hóa, không phải cấu trúc toán học. Chúng có múi giờ, giờ mùa hè, năm nhuận, giây nhuận và cải cách lịch. Chúng có các định dạng khác nhau ở những quốc gia khác nhau, các điểm khởi đầu khác nhau ở các thời kỳ khác nhau, và các ý nghĩa khác nhau trong các ngữ cảnh khác nhau. Và các tệp CSV, với việc hoàn toàn thiếu siêu dữ liệu, không cung cấp cho bạn cách nào để biết định nghĩa nào là đúng."Câu nói này từ một đồng nghiệp đã bắt trọn vấn đề về ngày tháng. Các tệp CSV không có kiểu dữ liệu. Chúng không có sơ đồ. Chúng chỉ đơn giản là văn bản. Khi bạn thấy "01/02/03" trong một tệp CSV, đó có phải là ngày 2 tháng 1 năm 2003? Ngày 1 tháng 2 năm 2003? Hay ngày 2 tháng 3 năm 2001? Ngày 3 tháng 2 năm 2001? Không có cách nào để biết mà không có ngữ cảnh, và ngữ cảnh chính là thứ mà các tệp CSV không cung cấp.
Các Số Không Phải Là Số
Đây là một bảng về những vấn đề dữ liệu số thường gặp nhất mà tôi đã gặp, cùng với tần suất và tác động điển hình của chúng:| Loại Vấn Đề | Tần Suất | Tác Động Điển Hình | Ví Dụ |
|---|---|---|---|
| Phân tách hàng nghìn | Rất Cao (60%) | Thất bại nhập, lỗi kiểu | "1,234.56" được phân tích thành chuỗi |
| Ký hiệu tiền tệ | Cao (45%) | Thất bại nhập, lỗi tính toán | "$1,234.56" hoặc "€1.234,56" |
| Sự khác biệt của dấu phân tách thập phân | Cao (40%) | Lỗi tính toán thảm khốc | "1.234,56" (Châu Âu) so với "1,234.56" (Mỹ) |
| Chỉ số khoa học | Trung Bình (25%) | Mất độ chính xác, hiểu sai | "1.23E+05" hoặc "1.23456789E-10" |
| Số không đầu | Trung Bình (30%) | Mất dữ liệu, hỏng ID | "00123" trở thành "123" |
| Các ký hiệu phần trăm | Trung Bình (20%) | Lỗi tính toán gấp 100 lần | "15%" được lưu là 15 thay vì 0.15 |
| Định dạng số âm | Thấp (15%) | Mất dấu, tính toán không chính xác | "(123 |