1
Training SQL
Nội dung
1. Cơ bản về SQL & Tối ưu kiểu
2. Tối ưu sử dụng index
3. Tối ưu câu lệnh query
4. Tối ưu thiết kế bảng
5. Các kỹ thuật mới nâng cao hiệu năng
2
• Tại sao query lại chậm?
• Tối ưu truy vấn dài
• Tối ưu kết hợp nhiều query
• Tối ưu COUNT() query
• Tối ưu GROUP BY và DISTINCT
• Tối ưu LIMIT và OFFSET
• Tối ưu UNION
• Tối ưu MIN và MAX
3
4
Tại sao query lại chậm?
Tại sao query lại chậm?
5
Tại sao query lại chậm?
• Tốn quá nhiều bộ nhớ
• Tốn quá nhiều CPU xử lý
• Network chậm
• Quá nhiều query sử lý cùng 1 lúc
6
Tối ưu truy vấn dài
• Lấy toàn bộ tên diễn viên trong bộ phim
Academy Dinosaur
7
mysql> SELECT * FROM actor
-> INNER JOIN film_actor USING(actor_id)
-> INNER JOIN film USING(film_id)
-> WHERE film.title = 'Academy Dinosaur';
Tối ưu truy vấn dài
• Viết lại
8
mysql> SELECT actor.* FROM actor...;
9
Tối ưu truy vấn dài
Tối ưu truy vấn dài
• Trường hợp bắt buộc phải lấy tất cả phải làm
thế nào?
10
mysql> SELECT * FROM actor
-> INNER JOIN film_actor USING(actor_id)
-> INNER JOIN film USING(film_id)
-> WHERE film.title = 'Academy Dinosaur';
Tối ưu truy vấn dài
11
SELECT * FROM tag WHERE tag='mysql';
SELECT * FROM tag_post WHERE tag_id=1234;
SELECT * FROM post WHERE post.id in (123,456,567,9098);
Tối ưu truy vấn dài
• Việc caching dữ liệu sẽ hiệu quả hơn
• Thực hiện lần lượt các câu lệnh tránh việc lock
tài nguyên
• Lấy ra kết quả sẽ dễ dàng hơn trong trường hợp
đặt database tại nhiều server
• Giảm thiểu số lượng các dòng dữ liệu phải kiểm
tra
12
Tối ưu truy vấn dài
• Câu truy vấn sau bị chậm do thực hiện
xóa hàng triệu bản ghi
13
mysql> DELETE FROM messages
-> WHERE created < DATE_SUB(NOW(),INTERVAL 3 MONTH);
Tối ưu truy vấn dài
14
rows_affected = 0
do {
rows_affected = do_query(
"DELETE FROM messages
WHERE created < DATE_SUB(NOW(),INTERVAL 3 MONTH)
LIMIT 10000")
} while rows_affected > 0
15
Tối ưu kết hợp nhiều query
Tối ưu kết hợp nhiều query
• Lấy tất cả film của actor = 1
16
SELECT * FROM sakila.film
WHERE film_id IN(
SELECT film_id
FROM sakila.film_actor
WHERE actor_id = 1
);
Tối ưu kết hợp nhiều query
• Lấy tất cả film của actor = 1
17
SELECT * FROM sakila.film
WHERE EXISTS(
SELECT film_id
FROM sakila.film_actor
WHERE actor_id = 1
AND film_actor.film_id = film.film_id
);
+----+--------------------+------------+--------+------------------------+
| id | select_type | table | type | possible_keys |
+----+--------------------+------------+--------+------------------------+
| 1 | PRIMARY | film | ALL | NULL |
| 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id |
+----+--------------------+------------+--------+------------------------+
Tối ưu kết hợp nhiều query
• Có thể sử dụng JOIN
18
SELECT DISTINCT film.id FROM sakila.film
INNER JOIN sakila.film_actor USING(film_id);
Query Kết quả queries per second (QPS)
INNER JOIN 185 QPS
EXISTS subquery 325 QPS
SELECT film_id FROM sakila.film
WHERE EXISTS(SELECT * FROM sakila.film_actor
WHERE film.film_id = film_actor.film_id);
Tối ưu kết hợp nhiều query
• Trường hợp ngược lại
19
SELECT film_id, language_id FROM sakila.film
WHERE NOT EXISTS(
SELECT * FROM sakila.film_actor
WHERE film_actor.film_id = film.film_id
)
SELECT film.film_id, film.language_id
FROM sakila.film
LEFT OUTER JOIN sakila.film_actor USING(film_id)
WHERE film_actor.film_id IS NULL
Tối ưu kết hợp nhiều query
20
Query Kết quả queries per second (QPS)
NOT EXISTS subquery 360 QPS
LEFT OUTER JOIN 425 QPS
21
Tối ưu COUNT() query
Tối ưu COUNT() query
• COUNT(col) và COUNT(*) khác nhau như
nào?
22
Tối ưu COUNT() query
• Count([col]) đếm số lần cột có giá trị khác NULL
• Count(*) không quan tâm đến cột, và chỉ đếm số
dòng kết quả trả về
23
Tối ưu COUNT() query
• Bảng 'goods_item' có 20.000 bản ghi với
cột id là khóa chính tự tăng.
• Có bao nhiêu bản ghi có id > 5?
24
id code name
1 0000001 sp1
... ... ...
20000 0020000 sp20000
Tối ưu COUNT() query
• Mất 0.012s
• EXPLAIN
25
Select COUNT(*) From goods_item Where id > 5;
id select_type type key rows Extra
1 PRIMARY range PRIMARY 9932 Using where; Using
index
Tối ưu COUNT() query
• Query được tối ưu như sau:
• Mất 0.007s
26
Select (Select Count(*) From goods_item) - Count(*)
From goods_item
Where id <= 5;
id select_type table rows Extra
1 PRIMARY goods_item 6 Using where; Using index
2 SUBQUERY NULL NULL Select tables optimized away
Tối ưu COUNT() query
• Câu query này lấy count chỉ sử dụng index
– Select Count(*) From goods_item
• Chỉ tìm ở nhánh <= 5
– Where id <= 5;
27
Tối ưu COUNT() query
• Lấy count của color là blue và red
• Làm thế nào để lấy count của của từng
color chỉ trong 1 query
28
SELECT COUNT(color = 'blue' OR color = 'red')
FROM items
Tối ưu COUNT() query
29
SELECT COUNT(color = 'blue' OR NULL) AS blue,
COUNT(color = 'red' OR NULL) AS red
FROM items;
SELECT SUM(IF(color = 'blue', 1, 0)) AS blue,
SUM(IF(color = 'red', 1, 0)) AS red
FROM items;
30
Tối ưu GROUP BY và
DISTINCT
Tối ưu GROUP BY và DISTINCT
• Query 1
• Query 2
31
Select actor.first_name, actor.last_name, COUNT(*)
From sakila.film_actor
Inner JOIN sakila.actor USING(actor_id)
GROUP BY actor.first_name, actor.last_name;
Select actor.first_name, actor.last_name, COUNT(*)
From sakila.film_actor
Inner JOIN sakila.actor USING(actor_id)
GROUP BY actor.actor_id;
Tối ưu GROUP BY và DISTINCT
• Nếu như first_name và last_name là unique thì
Query 2 nhanh hơn
• Tuy nhiên phải duyệt hết kết quả để count
32
Select actor.first_name, actor.last_name, COUNT(*)
From sakila.film_actor
Inner JOIN sakila.actor USING(actor_id)
GROUP BY actor.actor_id;
Tối ưu GROUP BY và DISTINCT
33
SELECT actor.first_name, actor.last_name, c.cnt
FROM sakila.actor
INNER JOIN (
SELECT actor_id, COUNT(*) AS cnt
FROM sakila.film_actor
GROUP BY actor_id
) AS c USING(actor_id) ;
Tối ưu GROUP BY và DISTINCT
• GROUP BY sẽ tự order
34
Select * From
(Select * From
(Select A.actor_id, A.first_name, A.last_name, F.release_year
From actor A
Inner join film_actor FA Using(actor_id)
Inner join film F On FA.film_id = F.film_id
Order by A.first_name, A.last_name, F.release_year desc
) actor
GROUP BY actor.actor_id
-- Sau khi GROUP BY sẽ tự động sort theo actor.actor_id.
) actor2
-- Phải sort lại một lần nữa.
ORDER BY actor2.first_name, actor2.last_name, actor2.release_year desc;
Tối ưu GROUP BY và DISTINCT
• Thêm ORDER BY NULL để skip sort
35
Select * From
(Select A.actor_id, A.first_name, A.last_name, F.release_year
From actor A
Inner join film_actor FA Using(actor_id)
Inner join film F On FA.film_id = F.film_id
Order by A.first_name, A.last_name, F.release_year desc
) actor
GROUP BY actor.actor_id
ORDER BY NULL
36
Tối ưu LIMIT và OFFSET
Tối ưu LIMIT và OFFSET
• Sử dụng cho bảng duyệt nhiều bản ghi và kết
quả trả về ít
37
SELECT <cols> FROM profiles INNER JOIN (
SELECT <primary key cols> FROM profiles
WHERE x.sex='M' ORDER BY rating LIMIT 100000, 10
) AS x USING(<primary key cols>);
Tối ưu LIMIT và OFFSET
• Sử dụng điều kiện với id để giới hạn bản ghi
• Lấy bản ghi từ 16049 quay về 16030
• 20 bản ghi tiếp theo
38
mysql> SELECT * FROM sakila.rental
-> WHERE rental_id < 16030
-> ORDER BY rental_id DESC LIMIT 20;
mysql> SELECT * FROM sakila.rental
-> ORDER BY rental_id DESC LIMIT 20;
39
Tối ưu UNION
Tối ưu UNION
• Câu query tối ưu chưa
40
(SELECT first_name, last_name
FROM sakila.actor
ORDER BY last_name)
UNION ALL
(SELECT first_name, last_name
FROM sakila.customer
ORDER BY last_name)
LIMIT 20;
Tối ưu UNION
41
(SELECT first_name, last_name
FROM sakila.actor
ORDER BY last_name
LIMIT 20)
UNION ALL
(SELECT first_name, last_name
FROM sakila.customer
ORDER BY last_name
LIMIT 20)
LIMIT 20;
42
Tối ưu MIN và MAX
Tối ưu MIN và MAX
• Lấy MIN actor, không có index cho first_name
43
SELECT MIN(actor_id)
FROM sakila.actor
WHERE first_name = 'PENELOPE';
Tối ưu MIN và MAX
44
SELECT actor_id
FROM sakila.actor USE INDEX(PRIMARY)
WHERE first_name = 'PENELOPE‘
LIMIT 1;
Tổng hợp
• Đảm bảo các query đơn giản nếu có thể
– Có thẻ cache
– Cũng không nên chia quá nhiều query
• Trong các trường hợp nhiều bản ghi hãy chủ ý
sử dụng LIMIT hiệu quả
• Đảm bảo rằng các cột trong điều kiện join ON,
USING phải được đánh index
• Cố gắngGROUP BY, ORDER BY một lần
45
Tổng hợp
• Luôn sử dụng EXPLAIN để điều tra câu query
• UPDATE version mới nhất cho RDBMS
46

Training sql3

  • 1.
  • 2.
    Nội dung 1. Cơbản về SQL & Tối ưu kiểu 2. Tối ưu sử dụng index 3. Tối ưu câu lệnh query 4. Tối ưu thiết kế bảng 5. Các kỹ thuật mới nâng cao hiệu năng 2
  • 3.
    • Tại saoquery lại chậm? • Tối ưu truy vấn dài • Tối ưu kết hợp nhiều query • Tối ưu COUNT() query • Tối ưu GROUP BY và DISTINCT • Tối ưu LIMIT và OFFSET • Tối ưu UNION • Tối ưu MIN và MAX 3
  • 4.
    4 Tại sao querylại chậm?
  • 5.
    Tại sao querylại chậm? 5
  • 6.
    Tại sao querylại chậm? • Tốn quá nhiều bộ nhớ • Tốn quá nhiều CPU xử lý • Network chậm • Quá nhiều query sử lý cùng 1 lúc 6
  • 7.
    Tối ưu truyvấn dài • Lấy toàn bộ tên diễn viên trong bộ phim Academy Dinosaur 7 mysql> SELECT * FROM actor -> INNER JOIN film_actor USING(actor_id) -> INNER JOIN film USING(film_id) -> WHERE film.title = 'Academy Dinosaur';
  • 8.
    Tối ưu truyvấn dài • Viết lại 8 mysql> SELECT actor.* FROM actor...;
  • 9.
    9 Tối ưu truyvấn dài
  • 10.
    Tối ưu truyvấn dài • Trường hợp bắt buộc phải lấy tất cả phải làm thế nào? 10 mysql> SELECT * FROM actor -> INNER JOIN film_actor USING(actor_id) -> INNER JOIN film USING(film_id) -> WHERE film.title = 'Academy Dinosaur';
  • 11.
    Tối ưu truyvấn dài 11 SELECT * FROM tag WHERE tag='mysql'; SELECT * FROM tag_post WHERE tag_id=1234; SELECT * FROM post WHERE post.id in (123,456,567,9098);
  • 12.
    Tối ưu truyvấn dài • Việc caching dữ liệu sẽ hiệu quả hơn • Thực hiện lần lượt các câu lệnh tránh việc lock tài nguyên • Lấy ra kết quả sẽ dễ dàng hơn trong trường hợp đặt database tại nhiều server • Giảm thiểu số lượng các dòng dữ liệu phải kiểm tra 12
  • 13.
    Tối ưu truyvấn dài • Câu truy vấn sau bị chậm do thực hiện xóa hàng triệu bản ghi 13 mysql> DELETE FROM messages -> WHERE created < DATE_SUB(NOW(),INTERVAL 3 MONTH);
  • 14.
    Tối ưu truyvấn dài 14 rows_affected = 0 do { rows_affected = do_query( "DELETE FROM messages WHERE created < DATE_SUB(NOW(),INTERVAL 3 MONTH) LIMIT 10000") } while rows_affected > 0
  • 15.
    15 Tối ưu kếthợp nhiều query
  • 16.
    Tối ưu kếthợp nhiều query • Lấy tất cả film của actor = 1 16 SELECT * FROM sakila.film WHERE film_id IN( SELECT film_id FROM sakila.film_actor WHERE actor_id = 1 );
  • 17.
    Tối ưu kếthợp nhiều query • Lấy tất cả film của actor = 1 17 SELECT * FROM sakila.film WHERE EXISTS( SELECT film_id FROM sakila.film_actor WHERE actor_id = 1 AND film_actor.film_id = film.film_id ); +----+--------------------+------------+--------+------------------------+ | id | select_type | table | type | possible_keys | +----+--------------------+------------+--------+------------------------+ | 1 | PRIMARY | film | ALL | NULL | | 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id | +----+--------------------+------------+--------+------------------------+
  • 18.
    Tối ưu kếthợp nhiều query • Có thể sử dụng JOIN 18 SELECT DISTINCT film.id FROM sakila.film INNER JOIN sakila.film_actor USING(film_id); Query Kết quả queries per second (QPS) INNER JOIN 185 QPS EXISTS subquery 325 QPS SELECT film_id FROM sakila.film WHERE EXISTS(SELECT * FROM sakila.film_actor WHERE film.film_id = film_actor.film_id);
  • 19.
    Tối ưu kếthợp nhiều query • Trường hợp ngược lại 19 SELECT film_id, language_id FROM sakila.film WHERE NOT EXISTS( SELECT * FROM sakila.film_actor WHERE film_actor.film_id = film.film_id ) SELECT film.film_id, film.language_id FROM sakila.film LEFT OUTER JOIN sakila.film_actor USING(film_id) WHERE film_actor.film_id IS NULL
  • 20.
    Tối ưu kếthợp nhiều query 20 Query Kết quả queries per second (QPS) NOT EXISTS subquery 360 QPS LEFT OUTER JOIN 425 QPS
  • 21.
  • 22.
    Tối ưu COUNT()query • COUNT(col) và COUNT(*) khác nhau như nào? 22
  • 23.
    Tối ưu COUNT()query • Count([col]) đếm số lần cột có giá trị khác NULL • Count(*) không quan tâm đến cột, và chỉ đếm số dòng kết quả trả về 23
  • 24.
    Tối ưu COUNT()query • Bảng 'goods_item' có 20.000 bản ghi với cột id là khóa chính tự tăng. • Có bao nhiêu bản ghi có id > 5? 24 id code name 1 0000001 sp1 ... ... ... 20000 0020000 sp20000
  • 25.
    Tối ưu COUNT()query • Mất 0.012s • EXPLAIN 25 Select COUNT(*) From goods_item Where id > 5; id select_type type key rows Extra 1 PRIMARY range PRIMARY 9932 Using where; Using index
  • 26.
    Tối ưu COUNT()query • Query được tối ưu như sau: • Mất 0.007s 26 Select (Select Count(*) From goods_item) - Count(*) From goods_item Where id <= 5; id select_type table rows Extra 1 PRIMARY goods_item 6 Using where; Using index 2 SUBQUERY NULL NULL Select tables optimized away
  • 27.
    Tối ưu COUNT()query • Câu query này lấy count chỉ sử dụng index – Select Count(*) From goods_item • Chỉ tìm ở nhánh <= 5 – Where id <= 5; 27
  • 28.
    Tối ưu COUNT()query • Lấy count của color là blue và red • Làm thế nào để lấy count của của từng color chỉ trong 1 query 28 SELECT COUNT(color = 'blue' OR color = 'red') FROM items
  • 29.
    Tối ưu COUNT()query 29 SELECT COUNT(color = 'blue' OR NULL) AS blue, COUNT(color = 'red' OR NULL) AS red FROM items; SELECT SUM(IF(color = 'blue', 1, 0)) AS blue, SUM(IF(color = 'red', 1, 0)) AS red FROM items;
  • 30.
    30 Tối ưu GROUPBY và DISTINCT
  • 31.
    Tối ưu GROUPBY và DISTINCT • Query 1 • Query 2 31 Select actor.first_name, actor.last_name, COUNT(*) From sakila.film_actor Inner JOIN sakila.actor USING(actor_id) GROUP BY actor.first_name, actor.last_name; Select actor.first_name, actor.last_name, COUNT(*) From sakila.film_actor Inner JOIN sakila.actor USING(actor_id) GROUP BY actor.actor_id;
  • 32.
    Tối ưu GROUPBY và DISTINCT • Nếu như first_name và last_name là unique thì Query 2 nhanh hơn • Tuy nhiên phải duyệt hết kết quả để count 32 Select actor.first_name, actor.last_name, COUNT(*) From sakila.film_actor Inner JOIN sakila.actor USING(actor_id) GROUP BY actor.actor_id;
  • 33.
    Tối ưu GROUPBY và DISTINCT 33 SELECT actor.first_name, actor.last_name, c.cnt FROM sakila.actor INNER JOIN ( SELECT actor_id, COUNT(*) AS cnt FROM sakila.film_actor GROUP BY actor_id ) AS c USING(actor_id) ;
  • 34.
    Tối ưu GROUPBY và DISTINCT • GROUP BY sẽ tự order 34 Select * From (Select * From (Select A.actor_id, A.first_name, A.last_name, F.release_year From actor A Inner join film_actor FA Using(actor_id) Inner join film F On FA.film_id = F.film_id Order by A.first_name, A.last_name, F.release_year desc ) actor GROUP BY actor.actor_id -- Sau khi GROUP BY sẽ tự động sort theo actor.actor_id. ) actor2 -- Phải sort lại một lần nữa. ORDER BY actor2.first_name, actor2.last_name, actor2.release_year desc;
  • 35.
    Tối ưu GROUPBY và DISTINCT • Thêm ORDER BY NULL để skip sort 35 Select * From (Select A.actor_id, A.first_name, A.last_name, F.release_year From actor A Inner join film_actor FA Using(actor_id) Inner join film F On FA.film_id = F.film_id Order by A.first_name, A.last_name, F.release_year desc ) actor GROUP BY actor.actor_id ORDER BY NULL
  • 36.
  • 37.
    Tối ưu LIMITvà OFFSET • Sử dụng cho bảng duyệt nhiều bản ghi và kết quả trả về ít 37 SELECT <cols> FROM profiles INNER JOIN ( SELECT <primary key cols> FROM profiles WHERE x.sex='M' ORDER BY rating LIMIT 100000, 10 ) AS x USING(<primary key cols>);
  • 38.
    Tối ưu LIMITvà OFFSET • Sử dụng điều kiện với id để giới hạn bản ghi • Lấy bản ghi từ 16049 quay về 16030 • 20 bản ghi tiếp theo 38 mysql> SELECT * FROM sakila.rental -> WHERE rental_id < 16030 -> ORDER BY rental_id DESC LIMIT 20; mysql> SELECT * FROM sakila.rental -> ORDER BY rental_id DESC LIMIT 20;
  • 39.
  • 40.
    Tối ưu UNION •Câu query tối ưu chưa 40 (SELECT first_name, last_name FROM sakila.actor ORDER BY last_name) UNION ALL (SELECT first_name, last_name FROM sakila.customer ORDER BY last_name) LIMIT 20;
  • 41.
    Tối ưu UNION 41 (SELECTfirst_name, last_name FROM sakila.actor ORDER BY last_name LIMIT 20) UNION ALL (SELECT first_name, last_name FROM sakila.customer ORDER BY last_name LIMIT 20) LIMIT 20;
  • 42.
  • 43.
    Tối ưu MINvà MAX • Lấy MIN actor, không có index cho first_name 43 SELECT MIN(actor_id) FROM sakila.actor WHERE first_name = 'PENELOPE';
  • 44.
    Tối ưu MINvà MAX 44 SELECT actor_id FROM sakila.actor USE INDEX(PRIMARY) WHERE first_name = 'PENELOPE‘ LIMIT 1;
  • 45.
    Tổng hợp • Đảmbảo các query đơn giản nếu có thể – Có thẻ cache – Cũng không nên chia quá nhiều query • Trong các trường hợp nhiều bản ghi hãy chủ ý sử dụng LIMIT hiệu quả • Đảm bảo rằng các cột trong điều kiện join ON, USING phải được đánh index • Cố gắngGROUP BY, ORDER BY một lần 45
  • 46.
    Tổng hợp • Luônsử dụng EXPLAIN để điều tra câu query • UPDATE version mới nhất cho RDBMS 46