Hiển thị các bài đăng có nhãn Database. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Database. Hiển thị tất cả bài đăng

SQL Tips: Tối ưu hóa câu lệnh MySQL - Phần 2

Tiếp phần 1 đã viết được khá lâu. Nay chúng ta sẽ tiếp tục viết phần 2 trong loạt bài tối ưu hóa câu lệnh MySQL

Vấn đề dư thừa dữ liệu trong câu truy vấn

Một nguyên tắc đơn giản nhất khi truy vấn dữ liệu là tránh sử dụng SELECT * trong bất kỳ trường hợp nào. Vì khi gọi lệnh này thì hệ quản trị sẽ tốn thêm tài nguyên để định danh những cột sẽ được lấy ra. Hãy chỉ rõ cột nào mình muốn lấy ra, giảm tải cho hệ thống và câu lệnh trở nên sáng sủa hơn.

Trong câu lệnh JOIN nhiều bảng thì cũng có chuyện dư thừa dữ liệu trong câu join. Bạn tìm hiểu thêm về tích descartes trong SQL thì bạn sẽ hiểu rõ hơn phần này. ví dụ đơn giản dưới đây:

VD: 

Bảng đơn hàng tb_Order có 300 bản ghi.

Bảng người mua tb_User có 1.000.000 bản ghi.

Bảng công ty tb_Company có 100.000 bản ghi.

Bạn cần lấy thông tin bản ghi thông tin mã hóa đơn, tài khoản người mua, tên người mua, tuổi, tên công ty của những khách hàng lớn hơn 18 tuổi trong năm 2021. 

Giả sử người dùng có thể thuộc hoặc không thuộc công ty nào cả. Ta sẽ phải viết query sử dụng câu lệnh JOIN như sau:

Select  a.OrderNumber,

                a.OrderDate, 

b.UserName,

b.FullName,

b.Age, 

c.CompanyName

from  tb_Order as a 

inner join   tb_User as b

on  a.UserID = b.UserID

left join tb_Company as c

on  b.CompanyID = c.CompanyID

where  b.Age > 18   

and a.OrderDate >= '202-01-01' and  a.OrderDate <= '202-12-31'


Nhìn câu lệnh này thì sau khi Join bảng a, b lại, số bản ghi câu lệnh phải duyệt ở mệnh đề where là  

300 * 1.000.000 = 30.000.000 bản ghi. Để giảm bớt số bản ghi lọc bởi where thì chúng ta nên viết lại thế này sẽ tối ưu hơn:

Select  a.OrderNumber,

                a.OrderDate, 

b.UserName,

b.FullName,

b.Age, 

c.CompanyName

from  tb_Order as a 

inner join   tb_User as b

on  a.UserID = b.UserID

and b.Age > 18   

left join tb_Company as c

on  b.CompanyID = c.CompanyID

where   a.OrderDate >= '202-01-01' and  a.OrderDate <= '202-12-31'


Trong một số trường hợp mà Left join thêm 1 loạt bảng khác để lấy thông tin thêm trên SELECT mà trong điều kiện WHERE không chứa điều kiện của bảng đó thì ta có thể viết dạng subquery để tăng tốc độ cho câu query. Chẳng hạn câu query:

Select  top 50 a.OrderNumber,

               a.OrderDate, 

b.UserName,

b.FullName,

b.Age, 

c.CompanyName,

d.CityName as CompanyCityName

from  tb_Order as a 

inner join   tb_User as b

on  a.UserID = b.UserID

and b.Age > 18   

left join tb_Company as c

on  b.CompanyID = c.CompanyID

left join tb_City as d

on d.CityID  = c.CityID

where   a.OrderDate >= '202-01-01' and  a.OrderDate <= '202-12-31'


Câu lệnh này nếu rơi vào trường hợp bảng Company và bảng City có rất nhiều dữ liệu (từ triệu bản ghi trở lên) sẽ chạy chậm hơn câu lệnh sau:

 select  ab.OrderNumber,

               ab.OrderDate, 

ab.UserName,

ab.FullName,

ab.Age,

c.CompanyName,

d.CityName as CompanyCityName

from (

Select  a.OrderNumber,

a.OrderDate, 

b.UserName,

b.FullName,

b.Age, 

b.CompanyID

from  tb_Order as a 

inner join   tb_User as b

on  a.UserID = b.UserID

and b.Age > 18   

where  a.OrderDate >= '202-01-01' and  a.OrderDate <= '202-12-31' 

                  order by a.OrderDate desc

                  limit 50 

) as ab

left join tb_Company as c

on  c.CompanyID = ab.CompanyID

left join tb_City as d

on d.CityID  = c.CityID


Thiết kế database theo hướng multi-tenancy, SaaS


Bài toán hướng multi-tenancy trong thực tế gặp rất nhiều, nhưng có rất nhiều developer chưa nắm được khái niệm và cách thức hoạt động của các hệ thống thiết kế theo hướng này. Qua một thời gian nghiên cứu và phát triển các hệ thống, mình đúc rút một số kinh nghiệm muốn chia sẻ cho mọi người.

Thực tế ta bắt gặp rất nhiều hệ thống sử dụng multi-tenacy
vd:
- Hệ thống quản lý cửa hàng cho phép nhiều đại lý có thể truy cập với những tài khoản độc lập, dữ liệu độc lập, nhưng cùng chung 1 hệ thống site.
- Hệ thống quản lý công văn sử dụng trong tổng công ty và nhiều công ty con, cùng site nhưng dữ liệu độc lập.
- Hệ thống quản lý dự án Jira
- Hệ thống CRM của zoho, saleforce...

Nhiều hệ thống sử dụng SQL server, Oracle ... thiết kế hệ thống multi-tenancy theo một trong các kiến trúc sau.


Phương án I. Cùng chung một cơ sở dữ liệu (database), chia sẻ bảng (table)
Ví dụ:
Một hệ thống quản lý cửa hàng, có bảng shop, bảng sản phẩm (product), bảng acccount

Bảng shop
Shop (
Id,
Name,
Notes)

Bảng user
User(
Id,
Name,
UserName,
Password,
ShopId
)
Bảng product
Product (
Id int,
Code varchar(50),
Name varchar(255),
ShopId)

Tất cả các bảng liên quan đều có 1 khóa ngoại là ShopId. Dữ liệu sản phẩm của từng shop đều được lưu chung trong bảng Product, nhưng được phân biệt nhau bởi trường ShopId.

Điểm mạnh:
- Thiết kế lưu trữ đơn giản.
- Dễ cho việc phát triển.
- Không gặp phải vấn đề đồng bộ cấu trúc bảng trong quá trình phát triền.

Nhược điểm:
- Không độc lập database nên việc một shop có thể xem dữ liệu của shop khác nếu có quyền truy cập SQL, phân quyền trên SQL thực sự là vấn đề lớn.
- Vấn đề backup, restore dữ liệu cho từng shop là gần như không thể, chỉ có thể backup cho tất cả.
- Vấn đề phát sinh thực sự phức tạp khi dữ liệu phình to, rất khó khăn trong việc backup, restore...
- Khó khăn khi scale hệ thống.

Lời khuyên: Phương án này chỉ dùng làm những hệ thống nhỏ, ít dữ liệu, phát sinh dữ liệu không lớn.


Phương án II. Cùng chung database, chia sẻ schema
Hướng thiết kế này sử dụng một cơ sở dữ liệu, mỗi tenant tương ứng 1 schema. Có một schema chung để quản lý những các dữ liệu chung, quản lý thông tin về tenants. Cấu trúc các bảng ở tất cả các tenant đều giống nhau.
Cần 1 schema chuẩn để dựa vào đó tạo ra tenant mới trong quá trình thêm mới tenant.

Điểm mạnh:
- Thiết kế theo hướng này thì có thê thay đổi các cấu trúc, hàm, thủ tục riêng rẽ giữa các tenant.
- Dễ phân quyền hơn phương án 1.
- Tiết kiệm được chi phí khi triển khai (do số lượng database chỉ là rất ít)

Nhược điểm:
- Phương án backup độc lập từng tenant là vấn đề nan giải, lập trình viên sẽ phải tự quản lý việc backup/restore cho từng tenant bằng code.
- Việc đồng bộ những thay đổi trong cấu schema là vấn đề cần phải quan tâm.
- Dữ liệu trong database sẽ phình ra nhanh chóng.
- Số lượng schema trong 1 database là có giới hạn.
- Khó khăn khi scale hệ thống.


Phương án III. Mỗi tenant một database.

Phương án này sẽ thực hiện như sau: hệ thống sẽ gồm 1 database chung (chuyên để quản lý các phần như danh sách tenant, user, role ...), 1 database tenant chuẩn (chứa dữ liệu chuẩn), và các tenant khác.
Mỗi tenant sẽ là 1 database, người dùng sẽ có quyền truy cập vào database chung và database tenant của user đó.

Mình sẽ đính kèm script sql server để tạo databases cho các phương án trên, phương án 3 giống như phương án 2, nhưng thay vì dùng schema thì chuyển sang dùng database.

Google Drive: SQL Script Mutil-tenancy












Like