Cải thiện hiệu suất của ứng dụng rails với cơ sở dữ liệu
Bài đăng này đã không được cập nhật trong 6 năm
Mở đầu:
Hiệu suất là một vấn đề ưu tiên lớn cho bất kỳ nhà phát triển nào. Tuy nhiên hầu hết mọi người thường phớt lờ về nó cho cho đến khi xẩy ra một problem nào đó liên quan hiệu năng ứng dụng của mình . Hiệu suất nên là một vấn đề mà chúng ta luôn phải đặt lên hàng đầu khi phát triển ứng dụng Nó cần phải là một phần của quá trình phát triển ứng dụng. Trong bài viết này, chúng ta sẽ cùng xem xét một vài cách chúng ta có thể làm để cải thiện hiệu suất ứng dụng rails của mình, bằng việc sử dụng Refactoring & Caching.
Hiệu suất cơ sở dữ liệu (Database Performance)
Khi bann sử dụng Rails, chúng ta thường sử dụng ORM - chẳng hạn như ActiveRecord hoặc DataMapper - giúp dễ dàng lấy dữ liệu từ ứng dụng. Tuy nhiên, chúng cũng thường làm cho việc chúng ta quên đi việc tối ưu hóa và tầm quan trọng của tái cấu trúc để cải thiện tương tác với database của mình. Nếu chỉ dựa vào các ORM để làm tất cả các công việc có thể dẫn đến các vấn đề sâu xa hơn trong vòng đời của ứng dụng. Khi chúng ta thêm các tính năng mới, chúng ta cần chú ý vào vào việc sắp xếp lại các truy vấn ORM. Một trong những vấn đề rất dễ xảy ra chúng ta khó phát hiện đó là "N + 1 Query". "N + 1 Query" nghĩa là một đối tượng đã được gọi và sau đó một đối tượng thứ hai vân được gọi, tạo ra một truy vấn thứ hai với cùng một kết quả vì vậy có thể bạn đang phải chạy 100 truy vấn để có được 1 kết quả thay vì chạy 1 truy vấn với 100 kết quả. Đây là một vấn đề khó phát hiện nếu cơ sở dữ liệu của bạn ở quy mô nhỏ vì vậy các vấn đề về hiệu suất rất khó nhận ra và chỉ có thể trở nên rõ ràng và dễ nhận thấy khi chuyển sang cơ sở dữ liệu có kích thước lớn.
có một cách thông dụng để trách N+1 là sử dụng eager loading. ví dụ so sánh:
# app/views/customers/index.html.erb
<% @customers.each do |customer| %>
<%= content_tag :h1, customer.name %>
<%= content_tag :h2, customer.addresses.first.city %>
<% end %>
Đoạn mã này sẽ tạo ra 101 truy vấn trong database có 100 customer.
Áp dụng eager loading bằng cách sử dụng .includes:
# app/controller.customers_controller.rb
class CustomersController < ApplicationController
def index
@customers = Customer.includes(:addresses).all
end
…
end
Điều này sẽ chỉ tạo ra 2 truy vấn trên 100 customer
Hiện nay có nhưng công cụ được sử dụng để phát hiện "N + 1 Query" như Bullet Gem... giúp chúng ta có thể dễ dàng phát hiện những truy vấn gây nên N+1. ngoài ra nó còn giúp bạn phát hiện những Eager Load thừa không được dùng đến
Một vấn đề khác có thể ảnh hưởng đến hiệu năng của cơ sở dữ liệu và hiệu suất ứng dụng đó là vấn đề truy vấn chậm. Truy vấn chậm được đề cập đến khi bất kỳ truy vấn nào kéo theo hiệu suất của cơ sở dữ liệu và mất nhiều thời gian hơn để xử lý. Nếu bạn đang sử dụng MySQL thì log này sẽ giúp bạn xác định một số vấn đề.
cat /db/mysql/log/slow_query.log
Nếu bạn đã biết chổ nào đang có vấn đề thì hãy cân nhắc việc thêm các chỉ mục index vào chổ đó. tìm kiếm có sử dụng chỉ mục trên 1 table với 1000 record se nhanh hơn 100 lần so với tìm kiếm với cùng 1 table nhưng không sử dụng chỉ mục index. Khi thêm một chỉ mục, điều quan trọng cần lưu ý là bảng sẽ khóa-vì vậy sẽ không có data được insert trong khi chỉ mục index đang được xây dựng.
Để thêm một chỉ mục ta sử dụng giống ví dụ sau:
class AddIndexForStuff
def change
add_index :stuff, :stuff_id
end
end
Bộ nhớ đệm (Caching)
Bộ nhớ đệm sẽ lưu trữ mọi thứ trong bộ nhớ để có thể tái sử dụng lại. Rails làm cho bộ nhớ đệm rất dễ hiểu và sử dụng, mặc dù tốt nhất là nên sử dụng nhưng ứng dụng bên ngoài mà không liên quan đến ứng dụng. Có thể sử dụng vài thứ như Nginx để lưu trữ các tệp tin tĩnh. sử dụng Rails và Nginx như sau:
# creates on #index, #show,
caches_page :index
# expires on #creates, #update, #destroy
expire_page :action => :index
Để phục vụ đúng các đối tượng cần lưu trữ trong bộ nhớ cache, cần phải thiết lập Nginx để làm dc việc việc này. bạn có thể làm như sau (ví dụ này giả định bạn đang sử dụng Unicorn làm máy chủ web của bạn. Để biết thêm thông tin về Rails webservers, xem ở đây ):
upstream upstream_enki {
server unix:/var/run/test001/unicorn_enki.sock fail_timeout=0;
}
location ~ ^/(images|assets|javascripts|stylesheets)/ {
try_files $uri $uri/index.html /last_assets/$uri /last_assets/$uri.html @app_enki;
expires 10y;
}
location / {
if (-f $document_root/system/maintenance.html) {return 503; }
try_files $uri $uri/index.html @app_enki;
}
Di chuyển những dư liệu tỉnh như ảnh và assets để có thể page-cache nhờ đó cải thiện dc performance. Nếu bạn không muốn sủ dụng page-caching thì vẫn còn một lựa chọn nữa đó là sử dụng memcache. Đây là kỹ thuật caching tiêu chuẩn trong Rails và dễ sử dụng. Đơn giản chỉ cần thiết lập bộ nhớ cache của bạn để mem_cache_store và thêm memcache servers như sau:
# config/intializers/memcached.rb
config.cache_store = :mem_cache_store,
"server-1:11211",
"server-2:11211",
"server-3:11211",
"server-4:11211"
Rails sẽ xử lý các hashing của các memcache ra để nó có thể có được thứ tự ưu tiên. Như trong ví dụ trên. chúng ta nên sử dụng nhiều máy chủ memcache để cải thiện hiệu suất tăng lên nhu mong đợi. Ngoài ra còn một kỹ thuật nữa là sử dụng Action-caching nó giống như bộ nhớ đệm trang. ngoài trừ toạn bộ nội dung của action đó sẽ được lưu trữ trong kho lưu trữ bộ nhớ cache. cái hay của nó là bất kyf before_filters nào vân sẽ được gọi. điều này sẽ đảm bảo bất kỳ chức năng nào ví dụ như xác nhận hoặc login vẫn sẽ được gọi trong khi các mục lưu trữ khác đa được lưu lại để cải thiện hiệu suất. ví dụ:
before_filter :make_sure_things_are_ok
caches_action :all_the_things
def all_the_things
@all_things = Thing.all_in_some_way
end
def expire
expire_action :action => :all_the_things
end
Sau cùng kỹ thuật Caching và Database Performance chỉ là 2 lĩnh vực ta có thể xem xét để tăng hiệu suất cho ứng dụng của bạn vì mục tiêu của nhà nhà phát triển đều là muốn cung cấp một trải nghiệm người dùng một cách tốt nhất có thể phải không
All rights reserved