Tạo bảng trong Redshift
Bài cuối cùng trong series về Redshift của mình. Trong 2 bài trước, một bài mình giới thiệu cấu trúc của AWS Redshift và một bài so sánh về performance giữa Redshift và MongoDB. Trong bài viết này mình sẽ giới thiệu về cách tạo bảng trong Redshift, set những options gì để tối ưu hóa performance, ...
Bài cuối cùng trong series về Redshift của mình. Trong 2 bài trước, một bài mình giới thiệu cấu trúc của AWS Redshift và một bài so sánh về performance giữa Redshift và MongoDB. Trong bài viết này mình sẽ giới thiệu về cách tạo bảng trong Redshift, set những options gì để tối ưu hóa performance, tận dụng được hết những ưu điểm của Redshift.
Vấn đề
Bài toán mình đã đặt ra ở bài viết so sánh Redshift và MongoDB. Bảng test_test có trường như sau:
client_id | date | url | url_hash | title | visit | pv | bounce | start | leave_coant | dwell_time | cv |
---|---|---|---|---|---|---|---|---|---|---|---|
c16012 | 2016-02-15 00:00:00 | /a | 6666cd76f961 | a | 4120 | 4386 | 1787 | 4120 | 1829 | 129571 | 0 |
c16012 | 2016-03-02 00:00:00 | /b | 6666cd76f962 | b | 5302 | 5302 | 1381 | 4991 | 1451 | 159885 | 0 |
c16012 | 2016-02-21 00:00:00 | /c | 6666cd76f963 | c | 5584 | 5881 | 2828 | 5584 | 1186 | 192301 | 0 |
c16012 | 2016-02-23 00:00:00 | /d | 6666cd76f964 | d | 5158 | 5543 | 4478 | 5158 | 1481 | 174318 | 0 |
cũng như vấn đề về 4 câu query. Thứ tự thực hiện query sẽ là tạo temporary table với câu lệnh
CREATE TEMP TABLE temporary_page AS SELECT url, title, sum(visit) as total_visit , sum(pv) as total_pv , sum(dwell_time_total) as total_dwell_time_total , sum(leave_count) as total_leave_count , sum(bounce) as total_bounce , sum(start) as total_start FROM test_test WHERE date BETWEEN '2016-02-12' AND '2016-03-25' AND client_id = 'c16012900556' GROUP BY url, title HAVING sum(visit) > 0
sau đó sẽ chạy 4 câu query vào temporaty table. Ở cuối bài viết đó thì với Redshift, tổng thời gian cho tạo temporary table cũng như 4 câu query là 10s. Cũng không tệ nhưng hoàn toàn có thể giảm được hơn nữa với Redshift.
Sort Key
Hản các bạn còn nhớ là Redshift lưu dữ liệu theo kiểu Columnar Storage, nghĩa là thay vì lưu data theo row thì sẽ lưu theo column. Lưu theo row như kiểu MySQL hay PostgreSQL thì bạn đánh index cho một trường nào đó mà bạn hay query vào trường đó giúp tăng performance. Redshift cũng tương tự vậy khi sử dụng khai báo sort key khi tạo bảng. Sort Key có thể cho 1 cột hoặc nhiều cột, Redshift sẽ sort cột đó khi lưu vào db giúp bạn tăng khả năng query, nhất là với query vào những cột đó. Những cột mà hay được chọn là sort key sẽ là những cột mà:
- Cột thường xuyên được query đến
- Nếu hay query tìm kiếm những dữ liệu gần đây nhất thì cột timestamp nên chọn làm sort key
- Cột được sử dụng để join với bảng khác thì nên chọn là sort key
SortKey của Redshift có 2 loại là Interleaved và Compound Sort Key.
- Compound Sort Key: chủ yếu được sử dụng nếu như bạn hay join, merge bảng. Nó cũng tăng performance của GROUP BY and ORDER BY. Đây là option của default của Redshift nếu như bạn không chỉ định loại Sort Key.
- Interleaved Sort Key: chủ yếu được sử dụng khi query với WHERE ở nhiều column. Với bài toán trên của mình thì không có Join hay Merge bảng. Cũng như câu lệnh query nặng nhất sẽ là ở WHERE chứ không phải ở group by do đó mình sẽ chọn Interleaved Sort Key (lưu ý Interleaved Sort Key chỉ có thể khai báo tối đa với 8 cột)
Trong ví dụ trên thì bảng test_test được query theo date và client_id WHERE date BETWEEN '2016-02-12' AND '2016-03-25' AND client_id = 'c16012900556' Do đó chúng ta sẽ chọn date và client_id là sort key cho bảng test test. Khai báo khi tạo bảng sẽ thêm option:
INTERLEAVED SORTKEY (date, client_id)
Distribution Key
Như bài trước mình đã giới thiệu thì Redshift sẽ có Leader Node và các Compute Node. Dữ liệu sẽ được phân chia đến từng Compute Node, các Compute Node lại có Slice và mỗi Slice thực hiện query giúp cho tăng khả năng query. Việc phân chia dữ liệu nào đến Compute Node nào thì tất nhiên là của Leader Node, mình không quan tâm =)) Tuy nhiên mình có thể chỉ định xem việc phân chia này sẽ dựa trên dữ liệu thông tin của cột nào trong bảng. Tại sao việc này lại quan trọng?
Rất đơn giản là nếu bạn không chọn distribution key hay chọn distribution key không hợp lý thì sẽ có compute node nhiều dữ liệu, có compute node lại ít dữ liệu, hoặc trong slice của mỗi compute node thì có slice có đến 20000 row nhưng có slice thì lại chỉ có 10000 row (À tất nhiên là nếu ban không chọn distribution key thì Redshift nó sẽ không để chênh lệch lớn thế này đâu =))) . Điều này sẽ ảnh hưởng rất lớn đến performance. Nó sẽ không thể nhanh hơn nếu như chọn distribution hợp lý sao cho mỗi slice có tầm 15000 row =))
Distribution Key thì có 3 loại sau
- Even distribution: loại default của Redshift. Việc distribution sẽ không dựa trên một column nào cả, Redshift sẽ round-robin đến từng compute node, từng slice. Even sẽ dược sử dụng nếu như bảng của bạn không join, hoặc là bạn không rõ ràng trong việc sử dụng 2 loại còn lại.
- Key distribution: Việc phân chia dựa trên giá trị của cột được chọn làm Distribution key.
- All distribution: Khác hoàn toàn với 2 loại trên, mỗi một slice sẽ lưu toàn bộ bảng. Nghĩa là bảng mà có 30000 row thì slice nào cũng có 30000 row. Điều này làm cho việc load, update, và insert dữ liệu là ác mộng =)) Tuy nhiên nếu bảng của bạn mà câu WHERE sử dụng rất nhiều column, và không có câu query nào hay dùng hơn câu nào thì chắc là phải chọn style này rồi =))
Trong bài toán trên của mình thì không thể chọn distribution là các cột url, title, pv, bounce... Mỗi một client_id thì cũng có lượng row rất khác nhau, nên mình sẽ chọn distribution key là date. Tất nhiên lượng dữ liệu trên mỗi compute node hay slice vẫn có sự khác nhau nhưng không nhiều. Ngoài ra mình cũng chỉ query theo client_id và date nên mình sẽ chọn loại distibution là Key. Khai báo khi tạo bảng sẽ là
DISTSTYLE key DISTKEY (date)
À đây sẽ là query để bạn check xem việc distribution trên bảng của bạn hiện tại là như thế nào
select slice, col, num_values as rows, minvalue, maxvalue from svv_diskusage where name = 'test_test' and col=0 and rows > 0 order by slice, col;
Compression Encodings
Redshift cung cấp rất nhiều kiểu encode để bạn nén dữ liệu là
- Raw
- Bytedict
- LZO
- Runlength
- Text255
- Text32K
Bạn có thể dùng câu lệnh query sau để check xem mỗi cột hiện tại đang chiếm bao nhiêu block trong Redshift, qua đó tìm kiểu encode hợp lý để nén dữ liệu
select col, max(blocknum) from stv_blocklist b, stv_tbl_perm p where (b.tbl=p.id) and name = 'test_test' and col < 12 # bảng của bạn có bao nhiêu cột thì col nhỏ hơn bấy nhiêu =)) group by name, col order by col;
và SQL để check kiểu encode cho từng cột trong bảng
select "column", type, encoding, distkey, sortkey, "notnull" from pg_table_def where tablename = 'test_test' and sortkey <> 0;
Sau khi check block cho từng column ở bảng của mình thì chả còn gì để nén nên phần này mình không thay đổi gì sau khi khai báo bảng =))
Kết quả
Sau khi đã hoàn thành 3 việc trên thì bảng của mình sẽ khai báo như sau khi tạo bảng
CREATE TABLE page_logs( # khai báo cột ở đây ) DISTSTYLE key DISTKEY (date) INTERLEAVED SORTKEY (date, client_id)
Và sau đấy là check xem hiện tại việc query mất bao nhiêu lâu. Lúc này thì thời gian query là
- Tạo temporary table: 5.424 s
- Data cho table: 280.472 ms
- Tỉnh tổng: 98.726 ms
- Graph: 180.365 ms
- Số lượng url: 20.471 ms
Tổng thời gian lúc này vào khoảng 6s =)) Rút ngắn được đến 4s so với kết quả trước đó.
Vấn đề cuối
Redshift có một hỗ trợ rất hay là bạn có thể review lại query vừa thực hiện. Từ thời gian chạy, query plan, CPU, Network... Và sau khi review lại query bên trên, mình thấy 1 một vấn đề đối với query tạo temporary page như hình dưới đây.
Tại sao lại bị như vậy? Như bài trước mình có nói thì Redshift sử dụng Internal network để truyền data giữa compute node và leader node. Lý do có thông báo trên là do có một lượng quá lớn data truyền qua internal network, dẫn đến kết quả cũng bị chậm lại một phần. Tất nhiên là bạn không thể tăng băng thông cho internal network được =))) Bạn chỉ có thể:
- Giảm lượng dứ liệu phải query xuống (với bài toàn của mình thì chọn khoảng thời gian ngắn đi chảng hạn). Cái này khó =))
- Encode column (khá là ok tuy nhiên bảng của mình thì không thể encode được nữa)
- Cấu trúc lại bảng, phân chia bảng thành bảng con (cái này áp dụng được =))
Mình sẽ cấu trúc lại bảng như thế nào? Rất đơn giản là mình sẽ tạo bảng con có id, client_id, url, title. Bảng test_test sẽ chỉ lưu id tương ứng của bảng con. Khi đó ở temporary table thay vì phải lưu cả url với title thì chỉ lưu id, và ở 4 câu query cuối cùng thì mình mới dùng id để lấy url và title. Qua đó giảm thiểu được lượng data truyền cho internal network.
Và sau khi làm xong thì mình thấy thông báo trên đã mất, ngoài ra thì tổng thời gian thực hiện chỉ còn 4,5s. Quá là ấn tượng (yeah)
Kết luận
Khi tạo bảng redshift thì bạn sẽ có những chú ý sau:
- Redshift không có đánh index
- Việc chọn sort key và distribution key chỉ có tác dụng khi tạo bảng, không thể thêm mới
- Việc chọn primary key và foreign key cũng không có ý nghĩa gì trong Redshift, trong Redshift thì chỉ có Sort Key và Distribution Key
À ngoài ra còn một phần nữa cần chú ý về Redshift là query nhanh là thế nhưng việc insert vào lại rất là chậm. Có 2 lý do như sau
- Dữ liệu phải qua leader node để xem là lưu ở compute node nào, slice nào (vấn đề distribution)
- Lưu theo columnar storage nên không đơn giản là chỉ việc copy paste dữ liệu cả 1 row block mà còn phải tìm block để lưu cho từng cột trong bảng.
OK, vậy là cũng xong chuỗi bài về Redshift (metqua). Hẹn gặp lại các bạn ở bài viết sau có thể sẽ tiếp tục là một service nữa của Amazon =)))