Xử lý theo lô (batch processing)

Xử lý theo lô (batch processing)

Tin ngắn hàng ngày dành cho bạn
  • Manus đã chính thức mở cửa cho tất cả người dùng rồi đấy mọi người. Cho những ai chưa biết thì đây là một công cụ viết báo cáo (làm mưa làm gió) giống như Deep Research của OpenAI á. Mỗi ngày được miễn phí 300 Credits để nghiên cứu. Mỗi lượt nghiên cứu tiêu tốn tuỳ thuộc vào độ phức tạp của yêu cầu. À với cả họ đang có chương trình tặng miễn phí Credits hay sao á. Như mình thì vào thấy được hẳn 2000.

    Mình dùng thử, so sánh với cùng một lệnh giống như đợt trước dùng bên Deep Research thì nội dung khác biệt nhau hoàn toàn. Manus báo cáo như kiểu viết văn hơn so với OpenAI là các gạch đầu dòng và bảng biểu.

    À lúc đăng ký xong có bắt nhập số điện thoại để xác minh, nếu lỗi thì các bạn đợi qua ngày thử lại xem có được không nhé.

    » Xem thêm
  • Mọi người chắc nghe nhiều về xu hướng tìm kiếm thông tin bằng AI chứ không cần công cụ tìm kiếm như Google nữa rồi đúng không? Không đâu xa ánh xạ vào bản thân thì thấy đúng thật, thi thoảng mới tìm kiếm thôi chứ còn đâu toàn hỏi tụi AI.

    Ngay từ đầu viết blog, thứ mà mình hướng đến là chia sẻ kinh nghiệm chứ không phải là những bài mang nặng tính kỹ thuật, máy móc, hướng dẫn từ đầu... Vì thời điểm đó đã có quá nhiều người làm nội dung này rồi và họ làm rất tốt, tại sao mình phải cố phát minh lại bánh xe? Một điều nữa là tin tưởng độc giả của mình có khả năng tìm hiểu vấn đề. Nếu bạn đọc đủ nhiều các bài viết trên blog thì thấy mình luôn cố gắng chèn thêm các liên kết tham khảo ngoài bài viết, nêu ra vấn đề mở và rất ít khi kết luận chắc chắn một điều gì đó.

    Mình đã cố gắng rèn luyện kỹ năng viết, kỹ năng trình bày và cả cách tương tác với độc giả để mang lại giá trị cho họ. Nhiều lúc ngồi lật lại các con số thống kê thấy lượng đọc bài viết tăng lên lại cảm thấy vui. Nhưng khi nguồn truy cập đến từ Google thì lại thấy buồn, vì điều đó chứng tỏ họ biết đến mình chỉ khi đang cố đi tìm giải pháp, có thể họ chỉ đọc chớp nhoáng, may ra tìm được cách giải quyết và thế là đóng cửa sổ trình duyệt rồi đi như một cơn gió.

    Chừng vài tháng đổ lại đây, một điều khiến mình rất vui đó là lượng người truy cập thẳng vào trang chủ mà không thông qua công cụ tìm kiếm đang tăng dần lên, có nhiều hôm lượng truy cập tự nhiên còn cao hơn cả đến từ Google. Điều đó chứng tỏ độc giả đã có thói quen quay lại trang của mình nhiều hơn và họ tìm thấy được giá trị từ blog mang lại. Vui mừng khôn xiết 🤩

    Bên cạnh đó thì lượng truy cập vào chuyên mục Threads - tức là mục mình đang viết bài này đang cao hơn bao giờ hết. Điều đó chứng tỏ xu hướng đi theo tin nhanh là đúng đắn. Mình có thể ngồi cả ngày để viết tin ngắn cho bạn đọc vì nó rất nhanh mà tiện, không tốn công đi tìm tài liệu để viết, không tốn cả thời gian viết nữa, còn mình thì có rất nhiều thứ để chia sẻ 😅. Nhưng không vì thế mà bỏ bê các bài viết dài, vì dài thì có nhiều thông tin để chia sẻ hơn.

    Vài lời tâm sự thế thôi chứ hơn một tháng nay mình chưa viết bài viết mới nào vì công việc bận quá. Xong lâu dần cứ trì hoãn lại thành lười. À với cả tháng 5 này rất thích hợp để đọc các cuốn sách về cách mạng á. Có hôm đọc đến 2 giờ sáng mới đi ngủ 🥱

    » Xem thêm
  • Mình mới nhìn thấy một trang web khá thú vị nói về các cột mốc đáng nhớ trong lịch sử phát triển Internet toàn cầu: Internet Artifacts

    Chỉ từ 1977 - khi Internet còn nằm trong hộp thí nghiệm thì nhìn xem - giờ đây Internet đã khiến mọi thứ phát triển đến mức nào 🫣

    » Xem thêm

Vấn đề

Có những chủ đề thành thật mà nói là rất khó để viết. Khó không phải vì nó khó (😀???), mà là do không biết trình bày làm sao cho nó thành một bài viết có hệ thống mà ai cũng dễ đọc, dễ hiểu. Từ khi biết đến câu nói "Bạn chỉ thực sự hiểu vấn đề nếu giải thích được cho người khác hiểu" của một vị tiền bối, tôi như bị ám ảnh và tuân theo như một mệnh lệnh trong sứ mệnh truyền đạt nội dung qua con chữ. Trong danh sách những chủ đề nháp, tôi đã bỏ lỡ biết bao nhiêu chỉ vì không đạt được mục đích đó. Nhưng không sao, có lẽ dần dần mình sẽ "thực sự hiểu vấn đề" ra thôi.

Xử lý theo lô (batch processing) cũng vậy, một vấn đề hơi rộng và có phần trừu tượng. Nhưng tôi tin chắc rằng một khi hình dung ra nó là gì thì quá trình xử lý thông tin sẽ diễn ra nhanh và mạnh mẽ hơn trong bất kỳ hệ thống nào. Do đó hy vọng rằng qua bài viết này sẽ giúp bạn đọc nhận ra những lợi ích đó!

Bây giờ, hãy bắt đầu với một câu chuyện đơn giản.

Vòng lặp (loop) là một trong những khái niệm cơ bản trong các ngôn ngữ lập trình. Phàm những gì là một danh sách đều có thể lặp qua để "duyệt" từng phần tử ở bên nó. Đôi khi cũng chẳng cần phải lặp qua một danh sách mà chỉ cần lặp qua một số n lần để làm một công việc gì đó cần n lần. Như bài toán Tính tổng dưới đây chẳng hạn.

let sum = 0;
for (i = 1; i <= 99; i++) {
    sum = sum + i;
}

Một bài toán kinh điển đúng không, tôi tin rằng ai mới tiếp xúc với lập trình đều phải viết được. Có lẽ thế mà xử lý lần lượt đã trở nên quen thuộc và đôi khi còn là một thói quen giải quyết bài toán lần lượt.

Lặp cho đến khi i nhỏ hơn hoặc bằng 99 có nghĩa là chúng ta đang thực hiện công việc 99 lần. Số đó thì nhằm nhò gì so với sức mạnh của một con chip thời nay: hàng tỉ tỉ phép tính trong 1 giây cơ mà. Đúng thế! Nhưng 99 chỉ là con số mà tôi ví dụ và sum cũng chỉ đơn giản là một phép cộng. Hãy nghĩ lại trong thực tế, logic trong vòng lặp của bạn phức tạp đến thế nào?

Dưới đây là một đoạn mã để tính tổng mà không cần lặp.

let n = 99;
let sum = (n * (n + 1)) / 2;

Tất cả đã được giải quyết trong một lệnh duy nhất. Một lô 99 phần tử gói gọn vào trong một phép tính. Và rõ ràng là nó hiệu quả hơn so với việc lặp đi lặp lại 99 lần. Vì thế, xử lý dữ liệu theo lô tức là giảm tần suất lặp xuống hoặc tăng lượng xử lý đồng thời lên để tăng hiệu năng cho ứng dụng.

Quay trở lại với một bài toán "thực tế" hơn một chút. Nơi bạn nhận được id của người dùng. Nhiệm vụ là lấy ra danh sách bài viết, bình luận, đồng thời đếm tổng số lượt thích của người này. Thiên thời địa lợi khi dữ liệu cần lấy đã được viết sẵn trong các hàm, dở một cái là nó là 3 hàm truy vấn trong 3 bảng khác nhau.

async function getArticles(userId) {
    ...
}

async function getComments(userId) {
    ...
}

async function countComments(userId) {
    ...
}

Lẽ thường tình, sẽ viết:

const userId = body.userId;
const articles = await getArticles(userId);
const comments = await getComments(userId),
const numComments = await countComments(userId);

Như vậy thì hơi tốn kém, vì numComments cần chờ comments, comments thì lại chờ articles, mà rõ ràng 3 hàm không phụ thuộc vào nhau. Chắc bạn sẽ nhận ra Promise có một hàm all để thực hiện nhiều Promise cùng thời điểm:

const userData = await Promise.all([
    getArticles(userId),
    getComments(userId),
    countComments(userId),
]);

Khi đó userData[0] chứa bài viết, userData[1] chứa bình luận và userData[2] chứa tổng lượt thích.

Các con số 0, 1, 2... khá là ẩn và không đầy đủ thông tin, thay vào đó tôi thích sử dụng Bluebird để làm sáng tỏ hơn một chút.

const userData = await Bluebird.Promise.props({
    articles: getArticles(userId),
    comments: getComments(userId),
    numComments: countComments(userId),
});

Khi đó userData.articles chứa bài viết, userData.comments chứa bình luận và userData.numComments chứa tổng lượt thích.

Thế là giải quyết xong một lô Promise!

Background job là cụm từ để chỉ những công việc cần xử lý dưới nền. Một phần nhằm tăng hiệu suất cho luồng chính, phần vì không phải công việc nào cũng cần xử lý ngay lập tức mà cần phụ thuộc vào thời gian. Lấy ví dụ như các công việc tổng hợp dữ liệu vào cuối mỗi ngày thì thường được background job xử lý.

Nhiệm vụ của bạn là mỗi 0h sáng cần thống kê lại một vài chỉ số của ngày hôm trước. Logic là thêm hoặc cập nhật cơ sở dữ liệu. Nếu theo thông thường, lấy ra danh sách người dùng (users), đếm ra tổng số lượt thích của mỗi người rồi tạo bản ghi thống kê vào cơ sở dữ liệu.

for (const user of users) {
    const numComments = await countComments(userId);
    await insertCountComments();
});

Với mỗi insertCountComments là thêm một bản ghi vào cơ sở dữ liệu. Như vậy nếu có 1 triệu users, bạn đang thực hiện 1 triệu lệnh ghi rời rạc vào cơ sở dữ liệu. Thay vào đó hãy gom các bản ghi lại và thực hiện thêm trong một lần duy nhất, hay còn gọi là "bulk insert" - được chứng minh là hiệu quả hơn so với ghi lần lượt.

const records = [];
for (const user of users) {
    const numComments = await countComments(userId);
    records.push(numComments);
});

await insertCountComments();

Thế là giải quyết xong bài toán thêm bản ghi vào cơ sở dữ liệu!

Khi làm việc với message queue, một Producer liên tục gửi thông điệp đến một hàng đợi, hàng đợi lại đẩy từng mẩu tin đó đến các Consumer đang được kết nối. Một vòng sản xuất - tiêu thụ được tuần hoàn như một dây chuyền sản xuất hiện đại.

Điều gì xảy ra khi các tin nhắn gần như là tương đồng với nhau về mặt nội dung và cả cấu trúc? Điều đó có nghĩa hành vi xử lý cho mỗi tin nhắn là gần như giống nhau. Mỗi một tin nhắn, chúng ta lại phải "chọc" vào cơ sở dữ liệu để lấy ra thông tin cần, trong khi nếu nhóm được các thông điệp lại với nhau sẽ giảm được đáng kể việc truy vấn.

Tuỳ thuộc vào công cụ đang sử dụng mà có hay không hỗ trợ gửi một lô tin nhắn. Ví dụ trong RabbitMQ bạn hoàn toàn có gửi một lô thông điệp trong một lần. Hành vi này theo họ nó là nó có thể tăng thêm vài chục lần thông lượng có thể xử lý so với cách xử lý thông thường là gửi từng gói tin.

Khi làm việc với Cloudflare, đặc biệt là Cloudflare Queues - tương tự như một message queue, còn có thể cấu hình đợi Producer gửi đủ số lượng tin nhắn thì mới đẩy một lô đó về Consumer. Ví dụ như là Producer có thể gửi từng thông điệp cho đến khi đủ 100 tin thì 100 tin đó mới được đẩy 1 lần đến Consumer xử lý.

Sau khi một lô tin nhắn về Consumer, chúng ta hoàn toàn có thể áp dụng tiếp quá trình xử lý dữ liệu theo lô nếu điều kiện cho phép.

Cuối cùng còn một trường hợp nữa mà xử lý theo lô phát huy sức mạnh!

I/O là các hành vi bất đồng bộ mà có nguy cơ xảy ra lỗi bất kỳ lúc nào, bởi các hoạt động I/O khá tốn kém và phụ thuộc nhiều vào khả năng xử lý của các yếu tố ngoài như tốc độ mạng, tốc độ phần cứng... Nếu không cẩn thận, chúng ta có thể gặp nhiều lỗi phát sinh trong quá trình xử lý.

Thay vì gọi hàm thực hiện liên tục các hoạt động I/O thì hãy thử giảm tốc độ xuống để giảm tải. Ví dụ như là hành vi ghi dữ liệu vào tệp (ghi logs chẳng hạn). Để ghi được một dòng logs, phải thực hiện các hành vi bao gồm mở file > ghi > đóng file thì hãy tổng hợp một lô dữ liệu thô và thực hiện việc mở > ghi > đóng trong một lần duy nhất.

Tổng kết

Trên đây là một số tình huống mà việc áp dụng xử lý theo lô mà tôi thường xuyên sử dụng. Ngoài ra, vẫn còn rất nhiều trường hợp áp dụng quá trình xử lý theo lô để tăng hiệu suất trong hệ thống thông tin. Bạn có biết thêm cách nào không? Hãy để lại bình luận phía dưới bài viết nhé!

Cao cấp
Hello

Bí mật ngăn xếp của Blog

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (0)

Nội dung bình luận...