Cấu trúc JSON lưu trữ ở trường dữ liệu properties của thực thể quiz

Một phần của tài liệu Xây dựng hệ quản trị đào tạo tích hợp mạng xã hội (Trang 120)

liệu properties của thực thể quiz.

Vì hệ thống hiện thực bằng ngơn ngữ Typescript nên việc làm việc với các cấu trúc JSON như trên khá đơn giản và dễ dàng. Ngồi ra, một ích lợi nữa của việc lưu trữ như trên là hiệu năng đọc sẽ được cải thiện khá nhiều.

Ngồi ra, jsonb cịn rất hữu ích trong trường hợp việc mơ hình thực thể gần như là bất khả thi. Lấy ví dụ thực thể Notification là thơng báo được gửi đi đến người dùng khi có một event nhất định nào đó xảy ra trong hệ thống. Ta nhận thấy là có hằng hà sa số các event kiểu như vậy, mỗi loại lại cần phải lưu trữ các trường dữ liệu khác nhau, phục vụ mục đích hiển thị cũng như chuyển trang khi người dùng bấm vào. Giải pháp đó chính là ta định nghĩa một trường dữ liệu properties kiểu jsonb lưu trữ được hết toàn bộ trường hợp và dựa vào một trường chỉ định (indicator field) để chỉ ra thực thể hiện tại thuộc loại nào mà xử lý cho phù hợp ở application logic. Vì tác vụ update vào các giá trị properties này là hầu như không bao giờ xảy ra, đây có thể xem là một ứng dụng phù hợp nhất cho kiểu dữ liệu jsonb. Kiểu dữ liệu này sẽ khơng phù hợp trong trường hợp dữ liệu có nhu cầu update một phần nhiều lần. Ở ví dụ bài quiz ở trên, vì nhóm làm đề tài hiện thực tác vụ update các câu hỏi của bài quiz bằng cách cho UI gửi lại tồn bộ thơng tin câu hỏi – đáp án dưới dạng JSON trả về server, hệ thống vẫn tránh được thao tác cập nhật một phần đối với kiểu dữ liệu này.

Hình 31: Lưu trữ thơng tin notification dưới dạng json

6.2.2 Phi chuẩn hóa dữ liệu

Chuẩn hóa dữ liệu là một kỹ thuật trong cơ sở dữ liệu nhằm loại bỏ dư thừa và loại bỏ các dị thường (anomaly) khi cập nhật và xóa dữ liệu. Vấn đề dư thừa khơng cịn là vấn đề quan trọng hiện nay khi mà bộ nhớ khơng cịn là một tài nguyên khan hiếm. Tuy nhiên, loại bỏ dị thường vẫn là một lợi ích quan trọng mà chuẩn hóa dữ liệu mang lại. Dị thường xảy ra khi dữ liệu được lặp lại ở nhiều chỗ khác nhau trong cơ sở dữ liệu. Khi đó, việc cập nhật nếu khơng cẩn thận chỉ cập nhật dữ liệu ở một nơi sẽ dẫn đến sự thiếu tính nhất quán trong hệ thống.

Một ví dụ thực tiễn cho việc này đã được nhóm làm đề tài rút ra được, đó chính là sự có mặt của các hệ cơ sở dữ liệu phụ như ElasticSearch (công cụ mà nhóm sử dụng để hiện thực chức năng tìm kiếm). Rõ ràng, chúng ta phải lưu trữ lại các thơng tin của thực thể trong cơ sở dữ liệu chính vào cơ sở dữ liệu để phục vụ mục đích tìm kiếm. Việc cập nhật này có thể được thực hiện đồng bộ, bất đồng bộ, hoặc thông qua một tác vụ lặp đi lặp lại. Dù cho cơ chế có như thế nào, logic cập nhật dữ liệu từ chỗ đơn giản thông qua một tác vụ UPDATE SQL thông thường trở nên phức tạp hơn rất nhiều. Tồi tệ hơn, các hệ cơ sở dữ liệu NoSQL như ElasticSearch thường khơng có khái niệm quan hệ. Do đó để có thể biểu diễn được mối quan hệ 1..N hay M..N trong sơ đồ ERD, ta thường phải sử dụng cơ chế lưu trữ danh sách các đối tượng lồng nhau (nested objects). Giả sử ta có một thơng tin mapping của trường dữ liệu lưu trữ danh sách thành viên của index Nhóm (Group) như sau:

Hình 32: Lưu trữ trường thành viêntrong index của thực thể group trong index của thực thể group

Khi tên của người dùng được cập nhật ở CSDL Postgres, chúng ta phải vào ElasticSearch tìm kiếm tồn bộ các nhóm có thành viên là người dùng trên, và cập nhật tên của đối tượng trong danh sách đối tượng lồng nhau members của từng nhóm có id trùng với id của người dùng. Do vậy, để hạn chế độ phức tạp trong việc hiện thực và quản lý hệ thống, chúng ta cần giữ cho mức độ lặp dữ liệu ở mức tối thiểu và chỉ sử dụng trong trường hợp cần thiết (như ở ví dụ trên, ta lưu thêm trường tên của thành viên trong nhóm để hiện thực nhu cầu lọc kết quả tìm kiếm nhóm theo tên của thành viên).

Tuy vậy, điều này khơng có nghĩa là ta ln ln phải tn thủ quy tắc chuẩn hóa dữ liệu trong việc thiết kế cơ sở dữ liệu có quan hệ. Nhược điểm của việc chuẩn hóa dữ liệu đó chính là sự đánh đổi về hiệu năng trong tác vụ đọc thơng tin. Một ví dụ được rút ra trong hệ thống chính là việc hiển thị danh sách các đối tượng của thực thể có hiện thực phân trang và sắp xếp có thứ tự.

Hình 33: Mối quan hệ giữa các thực thể Post –Vote - Comment Vote - Comment

Trong tác vụ lấy danh sách thông tin POST sắp xếp theo tổng số vote, ta phải thực hiện thêm một lần JOIN giữa post và vote để lấy ra được số lượng vote rồi mới sắp xếp và phân trang. Hiệu năng khi đó sẽ giảm xuống khơng chỉ vì có sự xuất hiện thêm của tác vụ JOIN mà

cịn vì ta khơng thể đánh index để tăng hiệu năng (nếu giả sử ta gặp phải vấn đề hiệu năng trong tương lai). Nếu chúng ta tạo một trường voteCount trong post lưu trữ số lượng vote trong post thì ta có thể đánh index vào trường này và tăng hiệu năng. Một ích lợi nữa của cách làm này đó đó là làm giảm độ phức tạp cho câu query. Giả sử ta muốn lấy thêm danh sách comment của từng bài post thì ta có thể viết được một câu query đơn giản là: SELECT * FROM post INNER JOIN comment … và lấy được cả số lượng post và comment cùng một lúc. Cách làm ngây thơ đó chính là join cả ba bảng và vừa đếm số lượng vote, vừa lấy thông tin comment trong cùng một câu query. Tuy nhiên, phép join như trên sẽ tạo ra tích Descartes giữa comment và vote, khiến cho việc thông tin comment bị lặp lại khi mapping lên application logic.

Nhóm làm đề tài cũng sử dụng kỹ thuật phi chuẩn hóa dữ liệu để hiện thực chức năng phân tích và hiển thị báo cáo đến người dùng. Đối với chức năng này, ta cần lưu trữ lịch sử các sự kiện và hoạt động cần thiết trong từng đơn vị thời gian xác định để có thể hiển thị báo cáo thống kê một cách linh hoạt: hiển thị thông tin theo nhiều chiều dữ liệu (dimension) và, với mỗi chiều dữ liệu, theo các khoảng thời gian tùy ý.

Như vậy, nhóm làm đề tài phân tích và thiết kế sơ đồ ERD cho tính năng báo cáo thống kê hoạt động của Nhóm như sau:

Hình 34: Thiết kế ERD cho tínhnăng hiển thị báo cáo trong Nhóm năng hiển thị báo cáo trong Nhóm

Thiết kế này được nhóm làm đề tài lấy cảm hứng từ mơ hình Star schema thường được sử dụng để phát triển kho dữ liệu (data warehouse). Mỗi một thực thể yếu có quan hệ với thực thể Group có thể được xem như là một chiều dữ liệu của Group, lưu trữ các thơng tin hoạt động của Nhóm theo thời gian. Ví dụ, thực thể Group Engagement lưu trữ tổng số lượng post,

comment và vote của toàn bộ thành viên trong nhóm theo từng ngày. Việc thu thập thơng tin được thực hiện thông qua việc chạy cron job ngầm ở phía server. Do đó, thời gian thu thập thơng tin (theo ngày) được chọn làm khóa yếu cho thực thể này. Việc lưu trữ như trên cho phép người dùng có thể lọc theo thời gian và biết được tổng số post, comment và vote trong bất cứ khoảng thời gian nào dựa trên hàm tổng hợp SUM. Các thực thể yếu trên cịn có tên gọi khác là dimension table, để phân biệt với các thực thể chính là fact table. Trong trường hợp này, phi chuẩn hóa dữ liệu thực sự cần thiết để đảm bảo hiệu năng cũng như tính đơn giản khi viết các câu lệnh query truy xuất thông tin thống kê theo thời gian.

6.3 Sắp xếp dữ liệu người dùng

Vì hệ thống tập trung vào trải nghiệm của người học là chính. Việc sắp xếp dữ liệu người dùng (user-generated contents) là tối quan trọng để đảm bảo trải nghiệm của người dùng. Đối với mỗi loại dữ liệu, nhóm làm đề tài lại có áp dụng và thử nghiệm một số phương pháp và giải thuật khác nhau.

Đối với các bài viết trong một nhóm thảo luận, nhóm làm đề tài sử dụng cơng thức sau để sắp xếp:

sign(v)×log(max(|v|,1))+

d

1000⌋−pivot

45000

với:

v là tổng số upvote trừ cho tổng số downvote của bài viết.

d là ngày bài viết được tạo (là giá trị thời gian UNIX tính bằng đơn vị milliseconds).

Nhóm làm đề tài chia giá trị này cho 1000 để ra được đơn vị seconds.  pivot là thời điểm bất kỳ nhỏ hơn mọi giá trị d trong hệ thống.

Cơng thức trên có là tổng của hai số hạng. Số hạng thứ nhất chỉ ra rằng, một bài viết có nhiều lượt vote tích cực hơn số lượng vote tiêu cực thì sẽ được đẩy lên đầu. Hàm số log có mặt để đảm bảo rằng việc tăng lượt vote tích cực tuân theo quy luật hiệu suất giảm dần (diminishing returns). Nhóm làm đề tài muốn lượt vote phải đóng góp vào thứ hạng của bài viết, nhưng cũng muốn là nó khơng lấn át q nhiều số hạng thứ hai: sự hao mòn theo thời gian (time decay). Sự hao mịn theo thời gian được tính bằng khoảng thời gian giữa ngày tạo bài viết và một giá trị gốc nào đó (tính bằng giây) chia cho 45000s. Ý nghĩa của cả cơng thức nói lên rằng: một bài viết có v là 100 sẽ có được xếp ngang hàng với một bài viết có v là 1000 nhưng được tạo vào 12 tiếng trước. Sở dĩ chúng ta khơng tính sự hao mòn bằng cách lấy thời điểm hiện tại trừ cho thời gian tạo là bởi khi đó, ta phải tính tốn lại cơng thức mỗi khi query vào cơ sở dữ liệu. Sử dụng công thức trên cho phép chúng ta phi chuẩn hóa, lưu trữ một trường hotness trong bài viết và chỉ tính tốn lại khi có lượt vote mới vì số hạng thứ hai đã được cố định. Ngồi công thức trên, bài viết cũng cho phép sắp xếp theo thời gian và theo tổng lượt vote (không bị ảnh hưởng bởi thời gian).

Đối với các bình luận ở bài viết, nhóm làm đề tài sử dụng cơng thức sau để sắp xếp:

^

p+zα2±zα2√[ ^p(1− ^p)+zα2/4n]/n

1+zα2/n

Cơng thức trên chính là giới hạn dưới của khoảng tin cậy điểm Wilson (Wilson Score

Confidence Interval). Giả sử rằng các lượt vote ở một bài bình luận (bao gồm vote tiêu cực và

vote tích cực) đại diện cho thích và khơng thích là một mẫu lấy từ toàn bộ dân số là số lượng người trong nhóm. Khi đó, việc sử dụng cơng thức trên có ý nghĩa là: với mẫu vote có được ở bình luận, thì với độ tin cậy là 95%, tỉ lệ thực sự các số lượng người thích trên tổng số người dùng trong nhóm ít nhất là bao nhiêu? Các kí hiệu trong cơng thức trên là:

 ^p là tỉ lệ số lượng vote tích cực trên tổng số lượng vote. Đây cũng chính là tỉ lệ của tham số mà ta cần tính khoảng tin cậy ở mẫu.

n là tổng số lượng vote (cũng chính là số lượng dữ liệu ở mẫu).

zα là z-score, thường dùng là 1.96 với độ tin cậy là 95%.

Hệ thống sử dụng cơng thức trên để sắp xếp bình luận để đảm bảo các bình luận cần phải có tỉ lệ vote tích cực trên vote tiêu cực lớn. Chúng ta không sử dụng tỉ lệ này trực tiếp làm công thức sắp xếp là bởi nếu như vậy thì các bình luận có ít vote tích cực nhưng tỉ lệ cao sẽ được sắp xếp lên trước các bình luận có nhiều vote tích cực nhưng tỉ lệ thấp hơn đôi chút. Điều này là sai bởi đối với các bình luận có lượng vote ít, thì tỉ lệ này sẽ rất ngẫu nhiên và khơng có khả năng phản ánh lên quan điểm của toàn bộ người dùng trong nhóm về bình luận. Hiện tượng cịn có tên là lời nguyền của kích thước mẫu nhỏ (the curse of small sample size).

Hệ thống không sử dụng công thức Wilson cho bài viết là bởi vì cơng thức này không thể đưa vào hiệu ứng suy giảm thời gian. Ảnh hưởng của thời gian đối với bài viết là quan trọng vì hệ thống muốn người dùng ln được tiếp cận với các bài viết mới nhất có thể.

Hệ thống cũng thực hiện việc sắp xếp nhóm mà người dùng đã tham gia và bạn bè của họ. Việc sắp xếp này xảy ra xuyên suốt trong các thành phần: ở hệ thống tìm kiếm (đẩy kết quả bạn bè lên đầu, nhóm mà mình đã tham gia lên đầu), hiển thị nhóm đã tham gia, hiển thị thành viên trong các nhóm được giới thiệu (sắp xếp bạn bè lên đầu), v.v… Hệ thống sắp xếp các nội dung này bằng việc giới thiệu một chỉ số có tên là interactionScore. Chỉ số này thu thập được từ tương tác của người dùng đến nhóm và bạn bè. Đối với nhóm, đó có thể là tạo bài viết, tạo bình luận, tạo vote. Đối với bạn bè, đó có thể là nhắn tin, xem trang thơng tin cá nhân, bình luận hoặc vote vào bài viết, bình luận của nhau. Hệ thống thu thập các tương tác này và tính tổng theo từng ngày (thông qua cronjob chạy cho từng ngày) và cập nhật

interactionScore dựa theo công thức sau:

score=∑ di wiCie−λ(d0−d) với:  i là một loại tương tác

Ci là số lượng của loại tương tác i trong một ngày

wi là trọng số của tương tác i hay mức độ quan trọng của tương tác i đến score cuối cùng.

d là ngày tương tác i được tạo.

e−λ(d0−d) là hệ số phân ra theo cấp số nhân của thời gian. Hệ số lambda sẽ được chọn sao cho sau 30 ngày thì hệ số này gần như bằng 0.

Nói cách khác, interactionScore là tổng phân ra theo thời gian của tổng có trọng số của các tương tác theo từng ngày.

Một vấn đề liên quan đến sắp xếp dữ liệu mà bất kỳ hệ thống nào cũng phải quan tâm đó chính là phân trang. Cách phân trang truyền thống được sử dụng rộng rãi đó là:

Hình 35: Request body trong trườnghợp phân trang truyền thống hợp phân trang truyền thống Khi đó, câu SQL query sẽ có dạng như sau:

Hình 36: Câu lệnh query sử dụng phân trangtruyền thống truyền thống

Tuy nhiên, cách làm trên sẽ không hợp lý trong trường hợp phân trang các dữ liệu mà thứ tự sắp xếp có thể thay đổi liên tục, ví dụ như bài viết và bình luận đã được trình bày ở trên. Thứ tự bài viết có thể thay đổi liên tục tùy vào lượt vote, do đó, khi người dùng xem các bài viết trong một nhóm và chuyển trang thì có thể gặp trường hợp một bài viết bị lặp lại do thứ tự của nó bị đẩy xuống dưới.

Hình 37: Request Body trong trường hợp phântrang con trỏ. trang con trỏ.

Cách phân trang này có tên là phân trang con trỏ (cursor-based pagination). Thay thế cho

page, chúng ta sử dụng anchor (hay cursor) là một param truyền vào và cũng là đối tượng trả

ra của API. Khi client query vào API này, đối tượng trả ra sẽ chứa anchor được tính tốn như sau: trong danh sách các kết quả trả về, lấy ở kết quả cuối cùng giá trị dùng để sắp xếp và id của nó rồi ghép lại bằng một ký tự separator nào đó. Khi client muốn lấy thêm một danh sách kết quả khác có thứ tự thấp hơn kết quả trước, anchor trả ra trước đó sẽ được gửi kèm theo. Hệ thống sử dụng anchor này cho câu lệnh SQL như sau:

Hình 38: Câu lệnh query sử dụng phân trang con trỏ

Cách phân trang này sẽ giải quyết được vấn đề lặp lại khi xem nội dung có thứ tự thay đổi liên tục.

6.4 Tìm kiếm

6.4.1 Xây dựng index

Hệ thống sử dụng ElasticSearch để hiện thực các chức năng tìm kiếm. Ở ElasticSearch, đơn vị tương ứng với bảng của SQL là một index. Cũng tương tự như bảng ở SQL, để tạo một

Một phần của tài liệu Xây dựng hệ quản trị đào tạo tích hợp mạng xã hội (Trang 120)

Tải bản đầy đủ (PDF)

(185 trang)