Làm việc khoa học (p1)

Tình cờ đọc trên facebook status của 1 bạn

Oracle chạy trên server 64G Ram, > 10 cores, insert 500,000 records mất 20 phút / 2 threads

Mongodb chạy trên laptop: insert 1,000,000 records / 40sec/1 thread

  total time speed
Oracle 500,000 records 20p = 1200 sec 410 records/sec
MongoDB 1,000,000 records 40 sec 25,000 records/sec

Table 1: tốc độ insert của Oracle vs MongoDB

MongoDB chạy trên laptop cùi nhanh hơn Oracle trên server xịn tới 60 lần –> MongoDB quá tuyệt vời!

A: Vậy mongodb tuyệt vời đến mức nào ?
B: nghĩa là sao ?

A: db là software, software tốt đến đâu thì cũng sẽ bị giới hạn bởi hardware đúng ko ? kiểu như chạy Call-of-duty trên card on-board ấy
B: uh ?

A: vậy thì hardware sẽ có 1 tốc độ insert tối đa nào đó (gọi là max_insert), so sánh tốc độ insert của db với max_insert sẽ thấy được hiệu suất (efficiency) của db
B: làm thế nào để biết tốc độ insert tối đa của máy?

Access time

Thường mọi người khi nhắc đến ổ cứng sẽ nói đến dung lượng (500GB, 1TB, 2TB), tốc độ (100MB/sec, 500MB/sec) hoặc loại (SSD vs HDD). Có một số liệu khác rất quan trọng (đặc biệt với database) của ổ cứng là access-time [1]

Access-time là gì ? có thể hiểu đơn giản là với mỗi lần đọc/ghi của ổ cứng sẽ có 1 độ trễ nhất định trước khi data được transfer (đọc/ghi). Và vì vậy access-time giới hạn tốc độ đọc ghi tối đa của ổ cứng.

  Time max IO/sec
HDD (laptop) 12-15ms 80
HDD (desktop) 10 ms 100
HDD server (15000 rpm) 4ms 250
SSD 0.1 – 0.5ms 10,000
SSD (hi-end) 0.005 – 0.01ms 200,000

Table 2: so sánh 1 số loại ổ cứng, access-time và số lần đọc/ghi tối đa mỗi giây

Max insert speed

Điều gì xảy ra ở mỗi insert transaction ?

  1. client gửi câu lệnh insert lên db-server
  2. db-server phân tích câu lệnh, tính toán gì đó ko biết được
  3. db-server ghi data trong lệnh insert xuống ổ cứng
  4. db-server trả về cho client: ghi thành công

Như vậy với mỗi lần insert sẽ cần ít nhất 1 lần ghi xuống ổ cứng (3) –> max_insert_speed <= max_io

  max insert database efficiency
HDD (laptop) 80/sec mongodb: 25,000/sec 31250%
HDD (server) 250/sec oracle: 410/sec 162%

Table 3: efficiency của 2 db

Chuyện gì vậy ? cả Oracle và MongoDB đều có tốc độ cao hơn tốc độ tối đa ? wtf ???

Trường hợp của Oracle: số thread để insert là 2 –> có thể Oracle đã merge 2 transaction cùng lúc ở 2 thread thành 1 lệnh ghi xuống ổ cứng –> code tốt!

  max insert database efficiency
HDD (laptop) 80/sec mongodb: 25,000/sec
1 threads
31250%
HDD (server) 250/sec oracle: 410/sec
2 threads
81% [2]

Table 4: efficiency của 2db – tính thêm ảnh hưởng của multi-thread.

Ok, con số của Oracle đã có vẻ hợp lí.

Vậy còn con số của MongoDB ?

MongoDB is way faster than MySQL and Postgres (insert), but there’s a tiny chance, like 1 in a million, that it “won’t save correctly”. [3]

Giải thích duy nhất là MongoDB không ghi data xuống ổ cứng sau mỗi insert. [4]

Như vậy không thể kết luận MongoDB nhanh hơn Oracle qua test này (do so sánh cam với táo) nhưng lại có thể kết luận Oracle an toàn hơn MongoDB 😛

Kết

Hãy làm việc 1 cách khoa học, test, benchmark, và benchmark đúng ; ). Cách tốt nhất để vượt qua giới hạn là biết rõ các giới hạn 😛

[1] xem thêm http://en.wikipedia.org/wiki/Disk_access_time#Access_time

[2] con số 81% của Oracle trên thực tế cao hơn nhiều vì ko phải lúc nào cũng có thể merge 2 transaction lại

[3] http://stackoverflow.com/questions/7149890/what-does-mongodb-not-being-acid-compliant-really-mean

[4] do config và có thể set lại

Enjoy the power of types (p1)

Static-type-language hay strong-type-language (Java, C#, VB.NET) cũng như OOP rất quen thuộc với developers. Tuy nhiên phần đông không tận dụng được sức mạnh đặc trưng của các language này: type-system.

Bắt đầu với 1 ví dụ đơn giản: ShoppingCart

Requirements

  • Add/remove item
  • Pay
  • Paid cart không thể add/remove/pay

Implementation thường thấy:

image

A: điều gì xảy ra khi add/remove/pay được gọi sau khi gọi pay?
B: exception, theo requirements.

A: requirements không hề nói đến việc sẽ có exception ở add/remove/pay.
B: ko thì làm thế nào để chặn được việc gọi sai add/remove/pay?

A: nếu add/remove/pay chỉ tồn tại lúc cart chưa paid thì sao?
B: nghĩa là ?

A: sau khi gọi pay, các method trên biến mất.
B: làm gì có cách nào bỏ 1 method ra khỏi 1 class ?

A: tại sao lại phải là 1 class?

Here we go ; )

Design with types

image image
Code sử dụng ShoppingCart

image
Chúng ta có gì ở đây ?

  • Không còn flag check
  • Không còn exception
  • Add/Remove/Pay chỉ có thể dùng “đúng”, code cũng đơn giản đi do ko cần quan tâm đến _paid
  • Error (exception) được handle ở đúng nơi nên handle (Controller)
  • Và điều tuyệt vời nhất: trước đây bạn sẽ gặp exception khi gọi add/remove/pay sau khi Paid; hiện tại bạn ko thể viết được đoạn code đó mà ko gây ra compiler-error!

Cool ? ; )

Summary

  • Tránh ôm đồm nhiều state trong 1 class giúp code đơn giản đi rất nhiều (6-70% trong 1 số trường hợp)
  • Không nên quá lạm dụng exception, bản chất của exception là unexpected – 1 code base có quá nhiều unexpected points có stable ko ?
  • Hạn chế tạo điều kiện cho code sai (và chặn việc đó = exception), hãy viết để code chỉ có thể dùng “đúng” –> code khó bug hơn rất nhiều

Bài đầu tiên trong series, hy vọng sẽ giúp các bạn “enjoy the power of type-system” hơn ; )