Làm thế nào để trở nên pro hơn trong mắt sếp ?
Một ngày đẹp trời, bạn đang say giấc giữa đêm khuya thì TING…tiếng chuông thông báo phát ra từ điện thoại khiến bạn giật mình tỉnh giấc và một cơn ớn lạnh kéo dài từ sống lưng tới đỉnh đầu khiến bạn phải rùng mình khe khẽ khi icon SLACK hiện ra trên màn hình điện thoại. THÔI TOANG RỒI – câu nói vang lên trong đầu bạn rõ mồm một như chatbox trên những trang truyện tranh. TING, TING… tiếng chuông thông báo lại kêu lên lần nữa rồi lần nữa, dồn dập tăng dần như nhịp tim bạn lúc này. Run run mở khoá màn hình điện thoại, tin nhắn hiện ra trước mắt với bộ dạng cáu kỉnh và đỏ au như màu mắt của mấy thằng coder thiếu ngủ: Batch fail – Server die
F*** – bạn nhủ thầm trong đầu và lồm cồm bò dậy với lấy cái máy tính. Màn hình hiện ra với những dòng chữ đỏ loè loẹt khắp nơi và lởn vởn trong đầu bạn hệt như khi mới chia tay mối tình đầu vậy. Pha vội cốc cà phê, bạn ngồi vào bàn và bắt đầu debug. Trước tiên, bạn bắt đầu truy cập vào server và check log hệ thống. Con batch chạy hàng ngày lấy dữ liệu từ media và đổ về Amazon RDS đã lăn ra chết. Trên màn hình hiện ra thông báo
Batch failed: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Communications link failure during commit(). Transaction resolution unknown
Sau một hồi stackoverflow thần chưởng và tham vấn ý kiến của ông anh trong nghề, bạn phỏng đoán lỗi xảy ra do kết nối keep connection quá lâu và có thể đã dẫn tới time-out. Giải pháp trước mắt đưa ra là retry lại phần này để chạy đủ dữ liệu cho khách hàng trước khi trời sáng. Nhưng đời vẫn là đời và nó vẫn thường lừa ta như giá Bitcoin gần đây vậy =((. Sau khi retry, batch chạy rất chậm và không có dấu hiệu có thể hoàn thành sớm trước khi trời sáng. Số lượng bản ghi insert vào database rất thấp : ~ 50rows/s
Bình tĩnh lại, bạn cùng ông anh bắt đầu ngồi lại và phân tích. Sau khi điều tra được nguyên nhân batch chạy chậm là do quá trình lưu dữ liệu vào database, bạn tiếp tục bắt tay vào đi sâu và đưa ra các nguyên nhân cụ thể hơn:
- Khi check log RDS: phát hiện có rất nhiều slow log, general log cho bạn thấy hiện tại hệ thống đang insert dữ liệu theo từng bản ghi. Sau mỗi câu lệnh insert, Mysql thực hiện commit ngay sau đó. Việc này dẫn tới khi thực hiện câu lệnh insert càng nhiều thì thời gian lưu dữ liệu vào database càng lớn.
- DB hiện tại đang sử dụng Aurora Mysql: Về lí thuyết, Amazon Aurora sẽ có tốc độ nhanh gấp khoảng 5 lần so với Mysql tiêu chuẩn theo quảng cáo của Amazon. Tuy nhiên trong một số, trường hợp việc sử dụng Aurora sẽ chậm hơn. Use case cụ thể ở đây là Aurora sẽ chậm hơn khi hệ thống có tỉ lệ cập nhật database cao với secondary indexes.
- Lượng dữ liệu đầu vào từ media tăng lên đột biến cũng có thể là khả năng.
Sau khi bàn bạc và xem xét hết các khả năng, bạn và ông anh cùng nhau thực hiện các action cụ thể:
- Tối ưu hoá code, loại bỏ hoặc thay thế những câu lệnh gây ra slow log. Thực hiện thay đổi cách thức insert dữ liệu để tối đa tốc độ.
- Dựng Mysql RDS mới để kiểm tra thử tốc độ lưu trữ và cập nhật dữ liệu so với Aurora
- Tạm thời loại bỏ secondary indexes để tăng speed và update lại sau khi chạy xong dữ liệu.
- Kiểm tra lại dữ liệu đầu vào
Kết quả:
- Sau khi test với Mysql, speed tăng lên đáng kể dù cũng chưa cao như kì vọng
- Dữ liệu đầu vào tăng một lượng lớn.
Và đến đây, để có thể trở nên pro trong mắt sếp =)) rất đơn giản, hãy dùng Bulk insert. Khi bắt tay vào làm một hệ thống liên quan đến dữ liệu, khả năng rất cao là lượng dữ liệu sẽ tăng dần theo thời gian và ngày càng lớn hơn. Vì vậy, việc sử dụng insert theo từng bản ghi không còn phù hợp với lượng dữ liệu ngày càng phình to và sẽ ngày càng làm chậm hệ thống của bạn. Thêm nữa, nếu database thường xuyên phải thực hiện các tác vụ truy xuất dữ liệu, việc sử dụng secondary indexes để tăng tốc độ truy xuất là cần thiết. Chính vì thế, cần lựa chọn loại database phù hợp với từng trường hợp cụ thể. Trên thực tế, sau khi đổi từ Aurora sang Mysql, tốc độ insert đã tăng đáng kể và giá tiền của Mysql thì cũng rẻ hơn khá nhiều. Hãy cũng xem tốc độ insert dữ liệu vào database sau khi sử dụng Bulk insert.
Tốc độ khi đạt đỉnh lên tới hơn 7000 rows/s và còn có thể tăng thêm tuỳ vào độ lớn dữ liệu đầu vào. Thời gian chạy batch giảm từ khoảng ~6h -> ~1h
Thật tuyệt vời, bạn cảm thấy mình thật pro, cảm giác như sắp thành ông tổ ngành code đến nơi nhưng không =)) tất cả thực ra sẽ rất đơn giản nếu như bạn bình tĩnh phân tích và bóc tách vấn đề, giải quyết từng phần nhỏ thì sẽ có khả năng giải quyết vấn đề tốt hơn và hiệu quả hơn. Tuy nhiên, việc sử dụng Bulk insert cũng hàm chứa một số rủi ro. Một trong những rủi ro bạn có thể gặp phải là không kiểm soát độ lớn đầu vào dữ liệu khiến cho câu lệnh insert của bạn trở nên quá dài và vượt quá size cho phép dẫn tới gây ra lỗi. Bên cạnh đó, cũng sẽ có những rủi ro khác khi bạn sử dụng phương thức này và các bạn có thể tìm thấy những lí giải khá là chi tiết và hợp lí tại đây. Vì vậy, hãy chỉ sử dụng khi bạn thực sự biết mình đang làm gì và nắm rõ các rủi ro có thể gặp phải để có thể đưa ra thiết kế hợp lí nhất tùy thuộc vào bài toán của mình.
Tổng kết lại
Khi gặp một vấn đề, hãy cố gắng nhìn nhận và bóc tách vấn đề đó theo nhiều hướng. Đó cũng là cách luyện tập để các bạn có thể “go pro” trong công việc của mình=)) Văn đến đây là cạn và trời cũng đã sáng. Chúc mọi người có một ngày làm việc vui vẻ và hiệu quả.
P/s: Nội dung bài blog chỉ có giá trị về phần kĩ thuật. Hình ảnh được tài trợ từ Performance Insights – Một tool khá hữu ích của Amazon RDS và chỉ supports MySQL version 5.7. 22 and higher. Câu truyện quá nửa là được sáng tác ra trong lúc nửa tỉnh nửa mê và có thể có ảo giác =)) Người đọc chống chỉ định thẩm văn trong lúc dùng đồ uống có cồn hoặc caffeine. Peace <3