At some point in your software engineer career, you will have to deal with data and your success depends on how big the data that your software can deal with. From a simple problem that requires processing a large amount of data, this talk will present to you how to approach this kind of issue and how to design and choose an efficient solution.
About speaker:
Hồ is Senior Software Engineer at AXON where he helps design and develops complex distributed systems, including image and video encoding, distributed file conversion system. Besides coding, Ho likes to read manga and meet friends in his free time.
10. How big is the data?
• A data set of 2 billion records of
unique URLs
• Assuming the previous program
needs 2 seconds to complete =>
Concurrency number = 0.5 URL/s
2 ∗ 2 ∗ 10𝑒8
3600 ∗ 24
= 46296 𝑑𝑎𝑦𝑠 ≈ 127(𝑦𝑒𝑎𝑟𝑠)
11. What is the concurrency number we need to
complete the dataset in X days?
12. What is the concurrency number we need?
• Goal: X=7 Days
• 2 billions URLs
• Current concurrency 0.5 URL/s.
2 ∗ 10𝑒8
X ∗ 3600 ∗ 24
=
2 ∗ 10𝑒8
7 ∗ 3600 ∗ 24
≈ 3307 𝑈𝑅𝐿𝑠/𝑠
13. How to increase concurrency?
• Optimize code performance
• Increase hardware resource (CPU,
RAM, Disk, Network…) aka Scale-
up
• Scale-out
• Cloning to multiple processes
(X-Axis)
• Splitting by functions (Y-Axis)
• Data partitioning (Z-Axis)
14. Optimize code
• Pros
• Most effective if we found a bottleneck that can increase performance
to 661,300%
• Save infrastructure cost
• Cons
• Time consuming and uncertain
15. Scale-up
• Pros
• Easy to apply
• Cons
• Take time to find out the suitable
hardware configuration
• Expensive and limited
• Still need to optimize code and
redesign to take advantage of
hardware resources when cannot scale-up
16. Scale-out by cloning (X-Axis)
• Pros
• Can use all hardware
resources
• Not limited by hardware
• Cons
• More complex than scale-up
• Concurrency problems
Node 1 Node 2 Node 3
18. Scale-out by Splitting (Y-Axis)
• Download and resize image using CPU
• Face detection on GPU is faster
Reference: https://sites.google.com/site/facedetectionongpu/
19. Scale-out by Splitting (Y-Axis)
X-axis: Cloning
Download and
Process Image
Download and
Process Image
Download and
Process Image
Face Detection Face Detection
Y-axis:Splitting
20. Scale-out by Splitting (Y-Axis)
• Pro
• Reuse the advantage of hardware
• Cons
• Complex
• Concurrency problems
21. Scale-out by data-partitioning (Z-Axis)
Data schema
ID URL Done
1 https://abc.com/image1.jpg 1
2 https://abc.com/image2.jpg 0
3 https://abc.com/image3.jpg 0
4 https://abc.com/image4.jpg 0
22. Scale-out by data-partitioning (Z-Axis)
ID URL Done
1 https://abc.com/image1.jpg 0
3 https://abc.com/image2.jpg 0
ID URL Done
2 https://abc.com/image2.jpg 0
4 https://abc.com/image4.jpg 0
Key hashing
23. Scale-out by data-partitioning (Z-Axis)
ID URL Done
1 https://abc.com/image1.jpg 0
2 https://abc.com/image2.jpg 0
ID URL Done
3 https://abc.com/image2.jpg 0
4 https://abc.com/image4.jpg 0
Range base
24. Scale-out by data-partitioning (Z-Axis)
• Pros
• Increase database performance
• Reduce locking/non-locking
• Cons
• Increase maintenance and infrastructure cost
• Hard for automation scaling
25. Summary
• Skip the code optimization approach
• Skip the scale-up approach
• Focus on scale-out approaches
• We can increase the number of
processes/machines to increase the
concurrency number
• We can split into 2 services: Downloader and
Face Detections
• We may need data partition to optimize
database performance
28. Race condition
• Cause
• Same URL process twice or
more
• Impact
• Waste of resources
• Data corruption
• Faking concurrency
29. Race condition: How to solve?
• Distributed locks
• Pros
• N/a
• Cons
• Pessimistic locking impact
performance
• Hard to apply because we need to
synchronize multiples nodes
• Not good fault-tolerance
• Data sharding
• Pro
• High performance because of share
load (Physical shard)
• Cons
• Hard for scaling
• Increase maintenance & infrastructure
cost
• Queue/Worker
• Pros
• Easy to implement
• Easy to scale
• Good fault-tolerance
• Reusable communnication
• Con
• The load concentrates on the
queue so it can become a
bottleneck
30. Race condition: root cause
Race condition only causes
between Downloaders
=> If we found a way to
distribute the unique URL for
each downloader it will solve
the race condition for the whole
system.
31. Fault Tolerance
• Faults
• Network fault
• Network interruption
• IP Blocking
• Service crash
• Problems
• Can data be lost?
• Can the service restart and
continue to work on remaining
tasks?
32. Fault Tolerance criteria
Given When Then
A service crashed It restarted No Rework (Continue on
remaining items only)
Downloader service is running It crashed All downloaded images should
not be lost
FaceDetector service is running It crashed All detected result should not be
lost
Downloader is downloading
image
Network error happens Retry
Downloader retry to download
an image again
Network error is IP Locking Should rotate proxy to change
the ip
34. Service communication methods
Type Method Pros Cons
Synchronous
HTTP • Familiar and
Simple to use
• Need a load
balancer
• Tight coupling
• Lock thread wait for
response
RPC • High performance
than HTTP
Asynchronous
Queue Messaging
(One-One)
• High performance
• Failure isolation
• Act as a load
balancer
• Reduced coupling
• Extra maintenance
cost
• Queue may
become bottleneck
Publich/Subscribe
(One-Many)
• We only need the
one-to-one
comunication
35. Summary
• Find approach to distribute unique URL to downloaders.
• The approach should pass the fault tolerance criteria
• We can base on the communication methods table to choose
the final solution
38. Approach 1: Range based physical shard
Solve
Race
Condition
Faul tolerance Comunication
Types
Notes
Solved + No rework
+ Need to download
image again if crash
when face detection
+ Partition can be
abadoned
HTTP/gPRC • Pros
• Non locking on db level
• Cons
• Take time for preparation
• Hard to scale out/adjust
• Need load balancer
40. Approach 2: Logical shard
Solve
Race
Condition
Faul tolerance Comunication
Types
Notes
Solved + No rework
+ Need to download image
again if crash when face
detection
+ Partition can be
abadoned
HTTP/gPRC • Pros
• Non locking on db level
• Simple implementation
• Cons
• Hard to scale out/adjust
• High database throughput
• Extra state to maintain: Total
Urls, Current Url Id,…
Chào các bạn
Chúng ta bắt đầu được chưa ạ?
MÌnh xin giới thiệu, mình là Hồ
Senior Software Engieer tại cty AXON
Ở axon mình “Write Code, Save Lives”
Về technical thì mình thích Thiết kế giải pháp và thiết kế code, ngoài ra mình còn thích nghiên cứu về video và image encoding.
Ngoài code ra thì mình cũng là một người “Bình Thường” thích nghe nhạc xem phim, đọc manga và xem anime.
Cho mình hỏi là ở đây có bạn nào từng phải suy nghỉ để tối ưu hóa code để chương trình chạy nhanh hơn chưa?
Nguyên nhân gì khiến bạn phải optimize code?
Hôm nay, mình muốn chia sẻ một vấn đề hết sức đơn giản trở nên khá là thú vị khi lượng data cần phải xử lí quá lớn mà mình từng gặp.
Mục tiêu là giúp các bạn có thêm nhiều góc nhìn trong việc giải quyết các bài toán trong quá trình làm việc ;)
Mình tin rằng lựa chọn công nghệ phù hợp sẻ giúp giải pháp của chúng ta tối ưu hơn, nhưng trong phần trình bày này mình sẻ không nghiên về việc lưa trọn công nghệ.
Mình không nói rằng các giải pháp của mình đưa ra là giải pháp tốt nhất.
Mình nhận được một yêu cầu là phải viết chương trình như thế này:
Nhận vào một URL của một tấm hình, Download tắm hình đó, xử lý và tìm vị trí của các khuôn mặt trên tấm hình đó.
=> Để dễ hiểu hơn, mời các bạn xem diagram của chương trình.
Tôi tin rằng, các bạn ở đây đều có thể viết được chương trình này.
=> và đây là code của chương trình
Nhưng mà, sẻ có bạn nói là, phần Face Detections khá là phức tạp nếu không có kiến thức về machine learning.
Đúng vậy, nhưng may mắn thay là Face Detection là vấn đề khá là phổ biến và bạn có thể dung thư viện có săn như: OpenCV hay Tensorflow….
=> và đây là code của phần Face Detections
Dùng code face detection có sẳn
Và đây là kết quả
Các bạn thầy bài toán ban đầu khá là đơn giản đúng không?
Nhưng đó chỉ là bài toán với 1 đường dẫn. Vậy nếu chúng ta có 2 tỷ đường dẫn thì sao? 2 tỷ đường dẫn lớn như thế nào? Mất bao lâu mới có thể xử lý hết?
Đó là những câu hỏi mà tôi đã đặc ra khi nhận được yêu cầu từ sếp là: Dùng chương trình ban đầu để xử lý hết 2 tỷ đường dẫn hình trong 1 tập dử liệu có sẳn.
=> Vậy giờ chúng ta cùng phân tích nha.
2 tỷ ảnh cần download
Nếu chương trình lúc nãy cần 2 giây để hoàn thành
Thì tôi cần 127 năm mới xử lý xong tập 2 tỷ ảnh.
Nếu tôi quay lại và nói sếp là cần 127 năm mới xử lý xong tập dữ liệu mà sếp đưa. Các bạn nghĩ sẻ như thế nào?
T = S/V
1 năm 365.25 ngày
Bài toán là: Tìm số concurrency number chúng ta cần là bao nhiêu để hoàn thành tập 2 tý ảnh trong số ngày mà chúng ta mong muốn?
Vì chúng ta cần phải tăng concurrency number từ 0.5 lên 3307 URL/s tức là tang khoảng 661300%
V=S/T
Theo kinh nhiệm của tôi thì chúng ta có 3 cách chính để tăng concurrency number.
Có rất nhiều cách để giúp bạn tang concurrency number. Nhưng tổng quảt lại thì có thể có 3 cách
Optimize code có thể giúp bạn tang concurrency
Tăng phần cứng, ví dụ: Tăng tóc độ xử lý của CPU, Tăng tóc độ đọc ghi của ổ đĩa hoặc tang RAM…
Hồi xưa tôi thường xúi khách hang tang IOPs của Database Sever để tăng tốc độ.
Scale-out (nhân rộng), có 3 phương thức scale-out
Nhân rộng ra nhiều nodes/processes
Chia theo chức năng
Chia nhóm dữ liệu đễ xử lý.
Chúng ta sẻ phân tích từng cách một
Cách tối ưu code có thể giúp chúng ta đạt được kết quả cực kì tốt nếu chúng ta tìm được thuật toán tối ưu hơn nhiều lần.
Nhưng chúng ta cần phải bỏ nhiều thời gian và công sức để tìm được chỗ cần optimize
Trong thời đại điện toán đám mây, bạn có thể có 1 con server cực mạnh chỉ cần vài click
Nhưng mà nó rất đắt đỏ (AZURE Calculator) https://st.ht/M6rGb
Và bạn sẻ đạt đến giới hạn sớm thôi
Tôi đã nghĩ cách này thì không có gì thú vi
Scale-out chiều ngang thì như mình đã nói, từ một node có sẳn bạn nhân ra thành nhiều node process dữ liệu cùng lúc.
Các này thì bạn sẻ không có bị giới hạn phần cứng
Khả năng chịu lỗi cao.
Nhưng mà khó triễn khai.
Ví dụ như race condition.
Đối với bài toán hiện tại thì Race condition có thể xãy ra như thế nào?
Scale-out theo chiều dọc là chia từ 1 node gồm tất cả các chức năng thành nhiều node mỗi node 1 chức năng.
Kiến trúc microservices dựa trên cách chia này.
Chúng ta cần xem xét lại các chức năng của chương trình để có hiểu được các chứng năng chính và lợi ích đạt được khi chia các chức năng ra.
May mắn là chương trình của tôi có các chức năng rất đơn giãn, vì thế tôi nhanh chia chương trình này ra thành 2 phần.
Có bạn nào có thể giúp tôi chia ra không?
Bằng cách chia thành 2 nhóm, thì hệ thống của tôi sẻ tận dụng được thế mạnh của phần cứng.
Đây là sơ đồ về các chia các nodes
Tới đây thì các bạn cũng thaas là từ một vất đề đơn giản ban đầu chúng ta đang phải gặp các vấn đề phức tạp hơn.
Nhưng mà chưa dừng lại ở đây. Có một số vấn đề cần giải quyết.
=> Chúng ta đã có hướng tiếp cận cơ bản là scale-out, nhưng còn một số vần đề cần phải quan tâm.
Tôi gọi nó là ”High Concurrency Problems” bới vì mục tiêu là giải quyết các vấn đề cần phải làm để tăng concurrency number.
Nhiều node cùng xử lý 1 URL.
Kết quả lưu vào có thể bị xáo chộn.
Vậy giải quyết race condition như thế nào?
Làm sao để đạt được performance tốt nhất?
Đây là vấn đề chúng ta cần phải quan tâm khi tìm giải pháp.
Để chánh việc phân phát các URL chùng lập. Chúng ta có 3 giải pháp.
Distributed locks
Có thể hiện thực bằng Redis Set/Get NX option
Data sharding
Là 1 cách thức scale-out
Các này cần phải tìm ra các để chia data cho hiệu quả.
Tốn chi phí chuẩn bị cũng như cơ sở hạ tân
Queue
Dễ thực hiện
Dễ scale
Để tiết kiệm thời gian, mình xin bỏ qua phần Distributed Locks.
Khả năng chịu lỗi là vấn đề tôi luôn nghĩ tới mỗi khi thiết kế hệ thống.
Tính đến các trường hợp lỗi có thể xãy ra giúp bạn giãm các rũi ro mà hệ thống của bạn có thể gặp phải.
Ví dụ: Mất data
Làm lại tư đầu
Làm thế nào để các service giao tiếp nhau?
Có cần một load balancer hay không?
Tiếp theo chúng ta sẻ tìm cách giải quyết vấn các vấn đề trên và tìm hướng tiếp cận.
Đê tiếp cận giải phấp tôi sẻ phân tích từng vấn đề và ở phần trước và cách giải quyết chúng.
Đầu tiên là Race Condition
Cost for preparation and deployment is high
Hard for scaling.
Add a new node: We can’t assign a new node to an existing partition. So we need to re-shard data and start again with new partitions number.
Remove a node (a node crash): The partition of this node can be abandoned.
Cost for preparation and deployment is high
Hard for scaling.
Add a new node: We can’t assign a new node to an existing partition. So we need to re-shard data and start again with new partitions number.
Remove a node (a node crash): The partition of this node can be abandoned.
he load is concentrated on a single database so the database can become the bottleneck (we can solve this problem by using more hardware resources for database node)
Hard for scaling:
Remove a node (or node crash): We need to recover the node (or add a new one with the same id) if we don’t want the partition of the node is abandoned
Add new node: restart all nodes in the system with new “Number Of Processes” value
Need to maintain the number of remaining URLs (or the processed URL)
the load is concentrated on a single database so the database can become the bottleneck (we can solve this problem by using more hardware resources for database node)
Hard for scaling:
Remove a node (or node crash): We need to recover the node (or add a new one with the same id) if we don’t want the partition of the node is abandoned
Add new node: restart all nodes in the system with new “Number Of Processes” value
Need to maintain the number of remaining URLs (or the processed URL)
The Delegator/Load Balancer is a service that fetches URLs from URLs database and pushes the URLs into the Queue which will be consumed by Workers (download & process image)
Queue: an abstraction, we can build this queue inside the Delegator/Load Balancer or using an open-source project like RabbitMQ/Kafka.
Process (1..N): Worker that consumes the URL from Queue for processing. Then push another queue item for the Face Detection phase.
With this approach, we have some advantages:
We archive Fault-Tolerance and Scalability naturally
When a worker fails, its URLs will be handled by other workers
When we add a worker, it will consume outstanding URLs in the queue
We can reuse the Queue system for Face Detection to optimize the reusable result of “Download & Process image”. This means we can save the image which was downloaded and processed in storage before putting the task item into Face Detection queue.
The implementation of single vs multiple goroutines in a node are the same, so we can have flexibility.
The Delegator/Load Balancer is a service that fetches URLs from URLs database and pushes the URLs into the Queue which will be consumed by Workers (download & process image)
Queue: an abstraction, we can build this queue inside the Delegator/Load Balancer or using an open-source project like RabbitMQ/Kafka.
Process (1..N): Worker that consumes the URL from Queue for processing. Then push another queue item for the Face Detection phase.
With this approach, we have some advantages:
We archive Fault-Tolerance and Scalability naturally
When a worker fails, its URLs will be handled by other workers
When we add a worker, it will consume outstanding URLs in the queue
We can reuse the Queue system for Face Detection to optimize the reusable result of “Download & Process image”. This means we can save the image which was downloaded and processed in storage before putting the task item into Face Detection queue.
The implementation of single vs multiple goroutines in a node are the same, so we can have flexibility.