1) Các yếu tố ảnh hưởng đến performance
Performance ảnh hưởng bởi hầu hết mọi yếu tố: Infrastructure, Network, Database, Quality Code,…
Ví dụ với cùng 1 cấu hình ở cùng 1 thời điểm, lưu lượng traffic truy cập vào cùng 1 con server cũng sẽ ảnh hưởng, vd 1 triệu request đồng thời khác với chỉ 1 request tới, thời gian response của 1 triệu request chắc chắn chậm hơn do phải phân bổ tài nguyên cho các request, thậm chí crash nếu vượt qua resource của server (tấn công DDOS là một điển hình). Hoặc như là vị trí đặt server, thay vì request gọi tới server đặt ở Việt Nam, trong cùng 1 vùng thì phải gọi qua khu vực quốc tế khác như Mỹ chẳng hạn cũng ảnh hưởng tới tốc độ response trả về.
2) Một số tools hỗ trợ monitor performance
Sử dụng các tool monitor performance để tìm ra các bottleneck ảnh hưởng đến performance.
- Application performance management: Newrelic, Skylight, AppSignal, …
- Google Page Speed: https://pagespeed.web.dev/
- Gems:
3) Nguyên lý improve performance
3.1) Giảm số lần gọi request
- Combine CSS, JS
- Use svg sprites
- CSS sprites
- Lazy load
3.2) Giảm thời gian phản hồi trên mỗi request
- Minify CSS, JS
- Caching
- Compress request
- Async/ Defer JS
- Optimize file size
4) Phân tích một số kỹ thuật
4.1) Async/ Defer JS
- async: Khi script load thì quá trình parse html vẫn diễn ra song song, chỉ đến khi Execute script thì parse html mới tạm dừng. Execute script sẽ thực hiện ngay sau khi việc download script hoàn thành. Do vậy quá trình thực thi sẽ không theo thứ tự load. Nên dùng trong trường hợp script không phụ thuộc vào các lib hay thành phần khác.
- defer: Khi script load thì quá trình parse html vẫn diễn ra song song, chỉ đến khi parse html hoàn thành thì Execute script mới chạy. Như vậy việc dùng defer thì parse html không phải tạm dừng. Execute script ở đây sẽ theo thứ tự load script. Nên dùng khi script phụ thuộc vào script hoặc các thành phần khác và ngược lại.
4.2) HTTP Compression
# Request header
Accepted-Encoding: gzip, deflate, br
# Response header:
Content-Enconding: gzip
Mặc định, khi setup server, HTTP compress đã được set mặc định, vd ở đây compress content body gzip.
4.3) HTTP Caching
Tham khảo bài viết: HTTP caching
4.4) Rails caching
Rails.cache.fetch(:key_name, expires_in: 1.day) do
Model.active.to_a
end
Chú ý: Rails sẽ không cache ActiveRecord:Relation vì ActiveRecord:Relation bản chất khi nào dùng tới thì câu SQL query mới thực hiện, nên như ở ví dụ trên, ta thêm .to_a để câu lệnh SQL thực hiện trả về array.
4.5) Database
4.5.1) Explain Analyze Query
Phân tích chiến lược thực thi của câu lệnh SQL để biết được từng bước mà SQL thực hiện các truy vấn, từ đó tìm cách tối ưu câu lệnh.
In Rails console
Post.joins(:comments).explain
In Rails dbconsole
4.5.2) Index Database
Ta có ví dụ:
Select * from posts where title = "Performance"
Khi không có Index cho cột title, query sẽ phải chạy qua tất cả các Row của bảng posts để so sánh và lấy ra những record thoả điều kiện title là “Performance". Vấn đề xảy ra khi bảng posts này qua thời gian, số lượng records tăng dần, nếu ko đánh index thì câu query sẽ càng ngày càng tốn nhiều thời gian để trả về kết quả.
Vì vậy index ra đời để giải quyết vấn đề trên, thay vì quét tuần tự toàn bộ bảng, index sẽ tạo 1 bảng mới, giúp lưu trữ một tập hợp các bản ghi dựa trên một số tiêu chí cụ thể và từ đó truy xuất chúng. Điều này làm cho quá trình truy xuất dữ liệu nhanh chóng hơn, giống như phần mục lục của 1 quyển sách, thay vì phải lật từng trang để tìm nội dung cần tìm, chỉ cần scan qua mục lục tìm số trang và lật tới ngay trang cần tìm mà ko cần phải tìm từng trang.
Mặc định thì khi tạo 1 table, primary key và foreign key sẽ được đánh index. trong trường hợp cần index 1 cột riêng, ta có thể add index cho nó.
Một số kiểu index phổ biến như:
- B-Tree(default): thích hợp với các queries >, <, LIKE, IN
Ví dụ: timestamp, number field,...
- Hash-index: thích hợp với các queries =, <>
Ví dụ: job-slug, token,...
- Gin-index: thích hợp với data structure như jsonb, hstore hay array
Ví dụ: metadata, object_changes,...
Làm thế nào để đánh index hiệu quả?
- Column thường xuyên sử dụng WHERE / JOIN
- Đánh Index cho column NOT NULL
- Không nên đánh index cho column thường xuyên thay đổi value
4.5.3) Partition Database
Mục đích: Chia table lớn thành cách table nhỏ hơn.
Một số kiểu partition phổ biến:
- Range Partitioning
- List Partitioning
- Hash Partitioning
class CreatePartitionedOrders < ActiveRecord::Migration[6.1]
def up
# Create the parent table but not store data
create_table :orders do |t|
t.datetime :created_at, null: false
t.decimal :total_amount, precision: 10, scale: 2
t.string :status
# Add other columns as needed
end
# Enable partitioning on the parent table
execute <<-SQL
ALTER TABLE orders
PARTITION BY RANGE (date(created_at));
SQL
# Create partitions for the next 12 months
12.times do |i|
start_date = Date.today.beginning_of_month + i.months
end_date = start_date + 1.month
execute <<-SQL
CREATE TABLE orders_#{start_date.strftime("%Y_%m")} PARTITION OF orders
FOR VALUES FROM ('#{start_date.to_s}') TO ('#{end_date.to_s}');
SQL
end
# Add any necessary indexes
add_index :orders, :created_at
add_index :orders, :status
end
end
4.5.4) Elasticsearch
Elasticsearch là một công cụ tìm kiếm dựa trên phần mềm Lucene. Nó cung cấp một bộ máy tìm kiếm dạng phân tán, có đầy đủ công cụ với một giao diện web HTTP có hỗ trợ dữ liệu JSON
4.5.5) Code quality
Reduce N+1 query
Có thể sử dụng gem bullet để dễ dàng detect N+1 queries trong code
Memorization
Memoization là một kỹ thuật tối ưu hóa, giúp tăng tốc các ứng dụng bằng cách lưu trữ kết quả của các lệnh gọi hàm (mà các hàm này được gọi là expensive function) và trả về kết quả được lưu trong bộ nhớ cache khi có cùng một đầu vào yêu cầu (đã được thực thi ít nhất 1 lần trước đó rồi).
# Memorize without argument
def user
@_user ||= User.last
end
# Memorize with argument
def cities(order_by)
@_cities ||= {}
@_cities[order_by] = City.order(order_by)
end
load_async
- sẽ thực hiện query ngay lập tức ở background job.
- nếu kết quả của load_async cần sử dụng ngay (vd ở view) thì câu lệnh load_async đó thực hiện ở main thread foreground luôn, không thực hiện ở background nữa
- thích hợp sử dụng với những câu query phức tạp tốn time-consuming mà kết quả chưa cần dùng tới ngay
3 Comments
Willard Mcinnis
2025-01-04 at 02:13AM
Are you struggling to reach your target audience? Let us help. We can blast your ad text to millions of website contact forms, ensuring that your message is seen by the right people. And with just one flat rate, you can reach a massive audience without worrying about per click costs.
Feel free to reach out via the info below for more information.
Regards,
Willard Mcinnis
Email: [email protected]
Website: http://gqneds.contactblastingworks.my
Wilford Littler
2025-01-05 at 08:31AM
Are you struggling to reach your target audience? Let us help. We can blast your ad text to millions of website contact forms, ensuring that your message is seen by the right people. And with just one flat rate, you can reach a massive audience without worrying about per click costs.
Reach out using the contact info below to learn more.
Regards,
Wilford Littler
Email: [email protected]
Website: http://fqdwpq.contactblastingworks.my
pqda54a09ae5b4bf117163b0e81f43559730c51816pq
2024-11-12 at 01:40PM
pq8db3b7a9da76a81655706cb1ac76968532dffb42pq