3. Các phương pháp tối ưu hóa cơ sở dữ liệu mức vật lý
4.2. Tối ưu trong viết lệnh truy vấn
Một số thủ thuật trong tối ưu truy vấn trên cơ sở dữ liệu như sau:
- Luôn tham chiếu đến các đối tượng bằng tên sở hữu đầy đủ trong four-part name, viết dbo.sysdatabases thay vì chỉ viết sysdatabases. (chỉ với SQL Server )
- Dùng các View hay Stored Procedure thay vì dùng những câu lệnh truy vấn dài. Cách này giúp chúng ta làm giảm giao dịch qua mạng, bởi vì máy khách chỉ cần gởi tên của View hay SP cho máy chủ( có thêm một số tham số) thay vì những dòng văn bản dài. Điều này cũng tạo điều kiện để quản lý phân quyền trong SQL Server, bởi vì bạn có thể hạn chế việc truy cập của các User đến những cột của một bảng mà họ không được truy vấn.
Báo cáo đề tài nghiên cứu khoa học và công nghệ cấp trường 2019
ThS. Hoàng Ngọc Cảnh – TT. Công nghệ thông tin 47
- Hạn chế việc viết và thực thi các câu truy vấn động cũng như viết mã truy vấn trong các chương trình. Thay vào đó hãy viết và thực thi trực tiếp trên môi trường của cơ sở dữ liệu thông qua các cửa sổ viết T-SQL, View, Stored, Functions.
- Hiểu được cơ chế và thứ tự thực hiện các lệnh Select, Join, Where, Group, Having, Distinct, Contrains, Top,… và đặc biệt hiểu về cách thức mà công cụ tối ưu của cơ sở dữ liệu chọn lựa thứ tự thực thi các mệnh đề điều kiện trong câu lệnh.
- Nên dùng Constraints thay cho Triggers, khi có thể. Constraints là hiệu quả hơn Triggers và giúp tăng tốc việc thực hiện. Vì vậy, bạn nên dùng Constraints thay cho Triggers ngay khi có thể.
- Dùng table variables thay vì dùng temporary tables. Table variables yêu cầu tài nguyên locking và logging ít hơn temporary tables. Vì vậy, Table variables nên được dùng ngay khi có thể.
- Nên dùng câu lệnh UNION ALL thay cho UNION, khi có thể. Câu lệnh UNION ALL là nhanh hơn UNION, bởi vì câu lệnh UNION ALL sẽ không tìm ra những dòng dữ liệu trùng nhau còn UNION sẽ tìm ra những dòng dữ liệu trùng nhau cho dù sự trùn đó có hay không.
- Nên tránh dùng mệnh đề DISTINCT, khi có thể. Bởi vì dùng mệnh đề DISTINCT kết quả thực thi sẽ chậm. Bạn chỉ nên dùng mệnh đề đó khi cần thiết.
- Tránh dùng Cursor trong SQL Server, khi có thể. Dùng Cursor trong SQL Server dẫn đến kết quả là làm giảm tốc độ hơn so với những câu lệnh Select. Cố gắng dùng các câu Select lồng nhau hay là dùng bảng tạm nếu muốn bạn muốn thao tác trên từng dòng dữ liệu.
- Nên tránh dùng mệnh đề HAVING, khi có thể. Mệnh đề HAVING dùng để giới hạn bớt kết quả trả về bởi mệnh đề GROUP BY. Khi dùng mệnh đề GROUP BY với mệnh đề HAVING, mệnh đề GROUP BY sẽ chia tất cả các dòng thành những tập hợp gồm nhiều tập hợp của các dòng và những giá trị của nó. Khi đó, mệnh đề HAVING sẽ hạn chế kết quả xuất ra không mong muốn của các tập hợp đó. Trong nhiều trường hợp, ta có thể viết câu lệnh SELECT mà chỉ có mệnh đề WHERE, GROUP BY không cần mệnh đề HAVING. Cách viết này sẽ cải thiện tốc độ câu truy vấn của bạn.
- Nếu chúng ta muốn lấy tổng số dòng trong bảng, chúng ta nên thay thế cách dùng câu lệnh thông thường là Select count(*). Bởi vì, câu lệnh Select count(*) yêu cầu thực hiện quét toàn bộ bảng để cho ra kết quả tổng số dòng. Điều này sẽ mất nhiều thời gian nếu bảng này chứa dữ liệu lớn. Có một cách khác để xác định tổng số dòng trong một bảng. Bạn có thể dùng bảng hệ thống là: sysindexes trong trường hợp này. Có cột tổng số dòng trong trong bảng. Cột này chứa tổng số dòng cho mỗi bảng trong
Báo cáo đề tài nghiên cứu khoa học và công nghệ cấp trường 2019
ThS. Hoàng Ngọc Cảnh – TT. Công nghệ thông tin 48
cơ sở dữ liệu.Vì vậy bạn có thể dùng câu lệnh sau thay vì dùng câu lệnh SELECT COUNT(*)
- Thêm câu lệnh SET NOCOUNT ON vào Stored Procedures để dừng thông báo về số dòng được thực thi bởi câu lệnh T-SQL. Điều này làm giảm giao dịch mạng, bởi vì máy khách sẽ không nhận được thông báo về số dòng bị tác động bởi câu lệnh T- SQL.
- Nên dùng mệnh đề WHERE để hạn chế bớt kết quả truy vấn. Điều này mang lại kết quả tốt cho các truy vấn. Bởi vì SQL chỉ trả về những dòng cụ thể cho máy khách, chứ không phải tất cả các dòng trong một bảng. Điều này làm giảm giao dịch qua mạng và tăng tốc câu lệnh truy vấn.
- Dùng những câu lệnh với từ khóa TOP hoặc câu lệnh SET ROWCOUNT nếu bạn muốn kết quả trả về chỉ là n dòng đầu tiên. Điều này có thể cải thiện tốc độ của các truy vấn, bởi vì tập kết quả trả về ít hơn được trả về. Điều này giảm bớt giao dịch giữa máy chỉ và máy trạm.
- Cố gắng hạn chế tập kết quả của các truy vấn bằng việc chỉ trả về những cột cụ thể từ một bảng, không phải tất cả các cột của bảng đó. Nếu làm vậy thì việc thực thi sẽ nhanh hơn bởi vì SQL Server sẽ chỉ trả về những cột cụ thể, không phải tất cả các cột của bảng. Nó sẽ giảm bớt giao dịch mạng và tăng tốc câu lệnh truy vấn.
- Sử dụng TRANSACTION khi thích hợp, và không cho bất kỳ người dùng nào tương tác vào khi transaction đang thực hiện.
- Cố gắng hạn chế sử dụng các toán tử sau trong mệnh đề WHERE: "IS NULL", "<>", "!=", "!>", "!<", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE", and "LIKE '%abc'". Vì những toán tử này nó không sử dụng đặc tính index mà thay vì thế nó sẽ dò tìm toàn bảng gây ảnh hưởng đến tốc độ của câu truy vấn. Nếu phải chọn lựa giữa IN và EXISTS trong câu truy vấn, hãy chọn EXISTS là tốt nhất, tương tự như IN và BETWEEN, hãy chọn BETWEEN.
- Khi sử dụng SQL động, hãy sử dụng sp_executesql với tham số được đặt tên (hơn là sử dụng EXEC hoặ EXECUTE), vì với thủ tục này, bạn có cơ hội sử dụng lại kế hoạch thực thi (query plan), tiết kiệm đáng kể thời gian cho trình biên dịch lập lại kế hoạch thực thi khi bạn chạy lại thủ tục tương ứng. (chỉ với SQL Server )
- Tránh việc cứ phải "đi qua đi lại" (round trips) giữa server và client, thay vào đó, hãy gửi các yêu cầu cần thiết một lần tới máy chủ thực thi và trả về một tập hợp nhiều kết quả là một trong những cách để giải quyết vấn đề này.
- Sử dụng tính năng Partition của database để phân chia dữ liệu logic database thành nhiều file lưu trữ vật lý, khi dữ liệu trong database hoặc table quá lớn. (Vì thao
Báo cáo đề tài nghiên cứu khoa học và công nghệ cấp trường 2019
ThS. Hoàng Ngọc Cảnh – TT. Công nghệ thông tin 49
tác truy xuất I/O của database là thao tác tốn rất nhiều hiệu năng của server và file dữ liệu càng lớn thì tốc độ truy xuất càng chậm và càng chiếm nhiều hiệu năng)
- Giới hạn thời gian câu truy vấn: Nếu người sử dụng thực hiện nhiều câu truy vấn đặc biệt trên SQL Server và bạn nhận thấy rằng các câu truy vấn được viết nghèo nàn này chiếm quá nhiều tài nguyên, nên cân nhắc sử dụng tuỳ chọn cấu hình ‘query governor cost limit’ để giới hạn thời gian câu truy vấn thực hiện. Chúng ta có thể gán option này với giá trị là seconds và có hai cách để xác định. Một là thay đổi nó ở cấp độ server bằng việc sử dụng sp_configure “query governor cost limit’” hoặc có thể gán nó ở cấp độ kết nối (chỉ có kết nối này có ảnh hưởng) bằng cách sử dụng lệnh SET QUERY_GOVERNOR_COST_LIMIT.
- Không nên sử dụng Optimizer hint (Index hint hoặc Join hint) trong câu truy vấn: vì thường thì rất khó đoán trước được Query Optimizer làm những gì. Optimizer hints là những từ khóa đặc biệt dùng để ép Query Optimizer chạy theo các từ khoá này.
- Sử dụng tối đa các loại index trong quá trình truy vấn đọc dữ liệu. Áp dụng nguyên tắc rất cơ bản trong SQL Server khi viết biểu thức điều kiện trong Where để tận dụng sức mạnh của Index, đó là cột cần tìm phải đứng một mình ở một phía của biểu thức tìm kiếm, nói cách khác là không có hàm số hay phép tính toán nào áp dụng trên cột đó, đây được gọi là cách viết sargable (Search Argument-Able).
Báo cáo đề tài nghiên cứu khoa học và công nghệ cấp trường 2019
ThS. Hoàng Ngọc Cảnh – TT. Công nghệ thông tin 50
CHƯƠNG 3: THỬ NGHIỆM VÀ ĐÁNH GIÁ MỘT SỐ GIẢI PHÁP TỐI ƯU HÓA TRÊN CƠ SỞ DỮ LIỆU THI TRẮC
NGHIỆM TRONG QUẢN LÝ ĐÀO TẠO TÍN CHỈ 1. Tối ưu thiết kế cơ sở dữ liệu thi trắc nghiệm học phần
Hệ thống phần mềm quản lý đào tạo tín chỉ là một hệ sinh thái số khá phức tạp với rất nhiều module chức năng như: Quản lý nhập học; Quản lý sinh viên; Quản lý đào tạo; Quản lý đăng ký học; Quản lý tổ chức thi; Quản lý điểm; Quản lý học phí; Quản lý thi trắc nghiệm trên máy tính,… Tất cả các module chức năng trên đều được kết nối đồng bộ đến cơ sở dữ liệu đào tạo tín chỉ chung, vì vậy trong phạm vi của bài báo, nhóm tác giả sẽ lựa chọn một module chức năng và nhóm các đối tượng cơ sở dữ liệu liên quan tới module đó trong cơ sở dữ liệu đào tạo tín chỉ chung để thực hiện một số phương pháp tối ưu. Cụ thể, module quản lý thi trắc nghiệm học phần trên máy tính cùng cơ sở dữ liệu thi trắc nghiệm sẽ được lựa chọn để tối ưu về thiết kế và truy vấn.
Một trong các ưu tiên thiết kế đối với cơ sở dữ liệu thi trắc nghiệm mà nhóm tác giả đưa ra như sau:
+ Cơ sở dữ liệu khi thiết kế phải bám sát luồng công việc diễn ra trong thực tế khi vận hành hệ thống. Hai lược đồ cơ sở dữ liệu phải được phân tách rõ ràng, gồm: lược đồ dữ liệu lưu trữ và xử lý ngân hàng câu hỏi thi; và lược đồ dữ liệu lưu trữ và xử lý bài thi.
+ Lược đồ quản lý ngân hàng câu hỏi thi cần xác định các nhóm thực thể sau: thực thể chính: Học phần, Câu hỏi, Đáp án; thực thể hỗ trợ: Loại câu hỏi, Mức độ câu hỏi, Chương/Nhóm kiến thức. Bên cạnh đó cần chú ý đến kiểu dữ liệu của các câu hỏi và đáp án vì trong thực tế dữ liệu thi khá đa dạng như: văn bản, số, công thức, bảng biểu, hình ảnh, âm thanh,… Chính vì vậy cần đa dạng hóa cách thức lưu trữ bằng nhiều kiểu khác nhau, như vậy sẽ tiết kiệm về mặt bộ nhớ và tăng tốc truy vấn dữ liệu sau này.
+ Lược đồ quản lý bài thi cần xác định các nhóm thực thể sau: Bài thi học phần sinh viên; Danh mục câu hỏi bài thi; Danh mục đáp án bài thi, với nguyên tắc một sinh viên chỉ có một bản ghi bài thi đối với một học phần, tiếp đó mỗi bài thi có danh mục n câu hỏi, cuối cùng mỗi câu hỏi có danh mục m đáp án trả lời. Trong đó chi tiết mỗi thực thể có khá nhiều các thông tin liên quan như ngày giờ, trạng thái, mức điểm, đúng, sai,… và danh mục các học phần, sinh viên, câu hỏi, đáp án hoàn toàn chỉ lưu khóa ngoại để tránh dư thừa dữ liệu, giảm kích cỡ bảng.
+ Tối giản kiểu dữ liệu của các thuộc tính nhất có thể sao cho phù hợp với quy mô dữ liệu; sử dụng số nguyên làm các khóa chính, khóa ngoại; đặt chỉ mục (index)
Báo cáo đề tài nghiên cứu khoa học và công nghệ cấp trường 2019
ThS. Hoàng Ngọc Cảnh – TT. Công nghệ thông tin 51
cho tất cả thuộc tính có thực hiện phép Join. Thực hiện các phép chuẩn hóa, đưa cơ sở dữ liệu về mức 3NF.
Hình 20: Lược đồ cơ sở dữ liệu thi trắc nghiệm học phần
2. Tối ưu truy vấn trong cơ sở dữ liệu thi trắc nghiệm học phần
Trong thực tế có rất nhiều quy tắc tối ưu truy vấn được áp dụng vào cơ sở dữ liệu thi trắc nghiệm học phần. Tuy nhiên nhóm tác giả sẽ tập trung phân tích một số giải pháp tối ưu trên một số Store Procedure về sinh đề thi và lấy bài thi vì đây là các bộ lệnh T-SQL tác động toàn diện đến các thực thể với yêu cầu truy vấn lượng dữ liệu khá lớn.
Với Store Procedure sinh đề thi, có một số phương pháp tối ưu như sau:
- Mục tiêu của bộ lệnh này nhằm tạo đề thi cho một tập sinh viên. Trong đó với mỗi sinh viên, thuật toán phải đảm bảo truy xuất vào dữ liệu tương ứng với học phần sẽ thi nằm trong bảng Câu hỏi và Đáp án một cách hoàn toàn ngẫu nhiên. Vấn đề lớn nhất trong thuật toán là phải thực hiện sinh đề lặp đi lặp lại nhiều lần trên các sinh viên khác nhau và truy vấn để tìm ra ngẫu nhiên được câu hỏi – đáp án trong một ngân
Báo cáo đề tài nghiên cứu khoa học và công nghệ cấp trường 2019
ThS. Hoàng Ngọc Cảnh – TT. Công nghệ thông tin 52
hàng dữ liệu quá lớn (lớn cả trên quy mô số lượng bản ghi và cả trên dung lượng một bản ghi).
- Sử dụng các bảng tạm (biến bảng) để rút gọn tập dữ liệu ngân hàng câu hỏi trước khi đưa vào truy vấn trong các lần lặp sinh đề thi như lệnh sau:
INSERT INTO @tblTapCauHoi(CH_ID, CH_MDCH_ID,
KCC_ID, CH_MucDiem)
SELECT tblCauHoi.CH_ID,tblCauHoi.CH_MDCH_ID,
tblCauHoi.CH_KCC_ID, tblCauHoi.CH_MucDiem
FROM tblCauHoi INNER JOIN
tblKhungChuongChinh ON
tblCauHoi.CH_KCC_ID = tblKhungChuongChinh.KCC_ID
WHERE CH_TrangThai = 1 AND
tblKhungChuongChinh.KCC_HP_ID = @HP_ID AND
tblKhungChuongChinh.KCC_BM_ID = @BM_ID
- Sử dụng việc sắp xếp mã GUI trên một tập dữ liệu nhỏ để lấy ngẫu nhiên câu hỏi và đáp án thay vì các thuật toán sinh ngẫu nhiên phức tạp. Kết hợp sử dụng phép lấy TOP dữ liệu (ví dụ số lượng câu hỏi là Top(50), số lượng đáp án là Top(4)) sẽ giúp tăng đáng kể hiệu năng thực thi của bó lệnh. Dưới đây là câu lệnh áp dụng kỹ thuật tối ưu đã nêu:
INSERT INTO
tblChiTietCauHoiDeThi(CTCHDT_SVLHPDT_ID, CTCHDT_CH_ID,
CTCHDT_MucDiem)
SELECT TOP(@TCDT_SoLuong) @SVLHPDT_ID, CH_ID,
CH_MucDiem FROM @tblTapCauHoi
WHERE KCC_ID = @TCDT_KCC_ID AND CH_MDCH_ID =
@TCDT_MDCH_ID
ORDER BY NEWID()
Với Store Procedure lấy đề thi, có một số phương pháp tối ưu như sau:
- Mục tiêu của Store này để lấy ra 50 câu hỏi cho mỗi sinh viên trong phòng thi, mỗi câu có 04 đáp án, như vậy tổng cộng có 200 bản ghi dữ liệu sẽ được liệt kê và gom nhóm theo thứ tự từ Câu 1 đến Câu 50. Vấn đề của bộ lệnh này là việc Join giữa
Báo cáo đề tài nghiên cứu khoa học và công nghệ cấp trường 2019
ThS. Hoàng Ngọc Cảnh – TT. Công nghệ thông tin 53
ba bảng dữ liệu SinhVienHocPhanDeThi với ChiTietCauHoiDeThi và ChiTietDapAnDeThi để lấy ra 200 bản ghi dưới dạng ID, sau đó tiếp tục Join với hai bảng CauHoi và DapAn để lấy ra nội dung câu hỏi và đáp án tương ứng với các ID vừa truy vấn được ở trên. Nếu cùng một lúc thực hiện Join 5 bảng trên để lấy dữ liệu thì tốc độ thực thi cực chậm do vấn đề lookup giữa các tập dữ liệu quá lớn vào nhau. Vậy cách làm ở đây thay vì Join liên tục, ta sẽ thực hiện giới hạn dữ liệu trên từng bảng, sau đó Join từng cặp 2 bảng lại với nhau với những điều kiện nhất định và kết quả thu được đặt thành bảng phụ mới. Cuối cùng sẽ đi Join các bảng phụ lại với nhau để ra danh sách cuối cùng. Cách làm này sẽ giúp giảm thiểu đáng kể dữ liệu ở mỗi nhóm bảng khi tham gia vào hai vế của mỗi lệnh Join.
DECLARE @tblA1 table(SoThuTuCauHoi nvarchar(20),
SVLHPDT_ID int, SVLHPDT_MaDeThi nvarchar(50),
SVLHPDT_ThoiGianLamBai smallint, SVLHPDT_ThoiDiemThi
datetime, SVLHPDT_TrangThai tinyint, SVLHPDT_DKDT
nvarchar(50), CTCHDT_ID int, CTCHDT_CH_ID int,
CTCHDT_MucDiem float)
DECLARE @tblA2 table(CTDADT_ID int,
CTDADT_CTCHDT_ID int, CTDADT_DA_ID int,
CTDADT_DapAnDung bit, CTDADT_DaChon bit)
Câu lệnh trên khai báo ra bảng phụ @tblA1 cho phép tiền xử lý lấy các dữ liệu thông số bài thi và danh mục câu hỏi, còn @tblA2 để lưu các danh mục cùng nội dung đáp án (do số bản ghi đáp án nhiều nhất, gấp tới 4 lần so với câu hỏi nên ta sẽ lưu ra