Trang chủ Một số kỹ thuật tối ưu Performance

Một số kỹ thuật tối ưu Performance

  • 2024 Sep 26
  • 8 minutes read

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.

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

pqda54a09ae5b4bf117163b0e81f43559730c51816pq

2024-11-12 at 01:40PM

pq8db3b7a9da76a81655706cb1ac76968532dffb42pq

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