+7

Tản mạn về select và pluck trong Rails

selectpluck là 2 method thuộc về ActiveRecord dùng cực nhiều trong quá trình làm việc với Rails. Hiểu rõ hơn về chúng sẽ giúp ta phần nào tăng hiệu suất của ứng dụng đang viết.

Đặt vấn đề

Đây là những khái niệm cơ bản, tuy nhiên nhiều new dev dùng nhưng không rõ bản chất của chúng, bài viết này không mang tính chất bổ sung kiến thức mà nó thiên về nhận xét, chia sẻ nhiều hơn.

Đầu tiên ta thí nghiệm với select

Npc.select :id
  Npc Load (61.8ms)  SELECT `npcs`.`id` FROM `npcs`
=> [#<Npc:0x007faabb082088 id: 1>,
 #<Npc:0x007faabb0948a0 id: 31>,
 #<Npc:0x007faabb0946e8 id: 61>,
 ...

select sẽ trả về cho ta một mảng các Npc model chỉ chứa giá trị của id


Giờ sẽ thử với pluck

Npc.pluck :id
   (63.3ms)  SELECT `npcs`.`id` FROM `npcs`
=> [1,
 31,
 61,
 91,
 121,
 151,
 181,
...

pluck sẽ trả trực tiếp cho ta một mảng số nguyên giá trị của id thay vì Npc model.


Ta có thể thấy chúng có câu truy vấn giống nhau (Thời gian lệch một chút, không đáng kể). Vậy xét về hiệu năng thì 2 method này tương đương nhau ? Thật ra là không phải như vậy, ta đang chỉ nhìn thấy bên ngoài chúng như vậy, còn sâu xa bên trong thì sao ?

Tính toán cẩn thận hơn chút

Hãy sử dụng Benchmark để đo thời gian thực tế khi sử dụng selectpluck

Đầu tiên là với select

puts Benchmark.measure {Npc.select(:id).to_a}
  Npc Load (62.4ms)  SELECT `npcs`.`id` FROM `npcs`
  1.900000   0.590000   2.490000 (  2.538390)

Giờ tới pluck

puts Benchmark.measure {Npc.pluck :id}
   (63.8ms)  SELECT `npcs`.`id` FROM `npcs`
  0.250000   0.070000   0.320000 (  0.367138)

Bạn có thể thấy thời gian chênh lệch thực sự là quá lớn, con số 62.4ms hay 63.8ms chỉ là thời gian truy vấn, nhưng Rails đã không cho ta thấy thời gian thật sự khi sử dụng chúng.

Nguyên nhân

Lý do dẫn tới thời gian chênh lệch nhau quá lớn như vậy là do sử dụng select ta sẽ mất rất nhiều thời gian để build lại các ActiveRecord models. pluck được sinh ra để phục vụ cho những nhu cầu cần số liệu cụ thể và ta cũng có thể thấy được hiệu suất khi sử dụng chúng rất khác biệt.

Phân biệt và ứng dụng

Việc sử dụng đúng nơi, đúng lúc chúng sẽ giúp chúng ta cải thiện được hiệu suất của ứng dụng. Những trường hợp điển hình ta dễ dàng phân biết chúng như select sẽ dùng khi ta cần đầu ra là ActiveRecord model hay dùng để chain truy vấn, trong khi pluck sẽ trả về thẳng cho chúng ta số liệu để phục vụ cho mục đích tính toán.

Ví dụ

Npc.where(npc_type_id: NpcType.select(:id))
  Npc Load (721.7ms)  SELECT `npcs`.* FROM `npcs` WHERE `npcs`.`npc_type_id` IN (SELECT `npc_types`.`id` FROM `npc_types`)

Npc.where(npc_type_id: NpcType.pluck(:id))
   (0.3ms)  SELECT `npc_types`.`id` FROM `npc_types`
  Npc Load (511.2ms)  SELECT `npcs`.* FROM `npcs` WHERE `npcs`.`npc_type_id` IN (1, 2, 3 ...)

Trên đây một ví dụ điển hình giúp ta phân biệt khi nào dùng select hay pluck. Với việc dùng select để chain truy vấn, ta chỉ cần query một lần vào database trong khi với pluck ta sẽ cần tới 2 query.

Kết luận

Trên đây là những kiến thức cơ bản, nhưng dễ dàng bị bỏ qua vì những quan niệm kiểu : Mình thích thì mình dùng thôi hay Nó chạy ngon nhanh thì mình dùng thôi. Qua bài tản mạn này, phần nào các bạn cũng biết được chúng nó khác nhau ở đâu, tại sao khác nhau , thời gian thực tế khi chạy cũng như sử dụng chúng đúng nơi đúng chỗ để đạt được hiệu suất tốt nhất.

Do kiến thức còn hạn chế nên bài viết tất sẽ có những sai sót, mong bạn đọc tích cực góp ý để mình hoàn thiện bài viết cũng như củng cố lại kiến thức.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí