{"id":928,"date":"2012-06-20T23:03:12","date_gmt":"2012-06-20T22:03:12","guid":{"rendered":"http:\/\/meier-online.com\/?p=928"},"modified":"2015-02-20T01:37:04","modified_gmt":"2015-02-20T00:37:04","slug":"rails_database_optimisation1","status":"publish","type":"post","link":"https:\/\/meier-online.com\/en\/2012\/06\/rails_database_optimisation1\/","title":{"rendered":"Optimize the Ruby-on-Rails database &#8211; part one"},"content":{"rendered":"<p><\/p>\n<h3>Reduce the number of database queries<\/h3>\n<p><a href=\"http:\/\/meier-online.com\/blog\/uploads\/rails-db-interaction1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-962\" title=\"Interaction of a Rails application with a database\" src=\"http:\/\/meier-online.com\/blog\/uploads\/rails-db-interaction1.png\" alt=\"\" width=\"600\" height=\"420\" srcset=\"https:\/\/meier-online.com\/blog\/uploads\/rails-db-interaction1.png 600w, https:\/\/meier-online.com\/blog\/uploads\/rails-db-interaction1-300x210.png 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/a><\/p>\n<p>A typical web application does many database queries before it delivers the response page to the web browser. The application needs to wait for the response of each of these database queries. It gets an additional slow down because of process switching. The database server must analyze each request, and also the communication between the database and the application takes time. So less database queries reduce the overall load on the system. The system scales better.<br \/>\n<!--more--><\/p>\n<h3>Container vessels and flag states<\/h3>\n<p>As an example, we implement a marine information system. The data model includes shipping companies (companies), vessels (container_vessels) and flag states (countries). A shipping company has a number of vessels, each vessel is registered in a flag state.<\/p>\n<p><a href=\"http:\/\/meier-online.com\/blog\/uploads\/vessel_db_model.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-942\" title=\"database model with  vessel, which belongs to company and to country\" src=\"http:\/\/meier-online.com\/blog\/uploads\/vessel_db_model.png\" alt=\"\" width=\"470\" height=\"162\" srcset=\"https:\/\/meier-online.com\/blog\/uploads\/vessel_db_model.png 470w, https:\/\/meier-online.com\/blog\/uploads\/vessel_db_model-300x103.png 300w\" sizes=\"(max-width: 470px) 100vw, 470px\" \/><\/a><\/p>\n<pre lang=\"ruby\"  escaped=\"true\">class Company &lt; ActiveRecord::Base\r\n    has_many :container_vessels\r\nend\r\n\r\nclass ContainerVessel &lt; ActiveRecord::Base     \r\n    belongs_to :company     \r\n    belongs_to :legal_country, :class_name =&gt; 'Country'\r\nend\r\n<\/pre>\n<p><a href=\"http:\/\/meier-online.com\/blog\/uploads\/vessel-table.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-944\" title=\"table of out marine information system\" src=\"http:\/\/meier-online.com\/blog\/uploads\/vessel-table.png\" alt=\"\" width=\"582\" height=\"366\" srcset=\"https:\/\/meier-online.com\/blog\/uploads\/vessel-table.png 582w, https:\/\/meier-online.com\/blog\/uploads\/vessel-table-300x188.png 300w\" sizes=\"(max-width: 582px) 100vw, 582px\" \/><\/a><\/p>\n<p>We now want to show a table with the vessels for a given shipping company. The table should contain all their vessels together with the flag state of the vessel. A straight-forward implementation just uses the associations of the model.<\/p>\n<p>In the controller, the vessels will be retrieved:<\/p>\n<pre lang=\"ruby\">@company = Company.find(params[:company_id])\r\n@container_vessels = @company.container_vessels.order(:name)<\/pre>\n<p>And in the view we just access to the country through the association<\/p>\n<pre escaped=\"true\">&lt;% @container_vessels.each do |vessel| %&gt;\r\n  &lt;%= vessel.name %&gt;\r\n  &lt;%= vessel.legal_country.name if vessel.legal_country.present? %&gt;\r\n&lt;% end %&gt;<\/pre>\n<p>The solution works and is easy to understand. Let&#8217;s see what database queries are triggered:<\/p>\n<pre lang=\"sql\">SELECT \"companies\".* FROM \"companies\" \r\n        WHERE \"companies\".\"id\" = ? LIMIT 1 [[\"id\", \"3\"]]\r\nSELECT \"container_vessels\".* FROM \"container_vessels\" \r\n        WHERE \"container_vessels\".\"company_id\" = 3 ORDER BY name\r\nSELECT \"countries\".* FROM \"countries\" WHERE \"countries\".\"id\" = 4 LIMIT 1\r\nSELECT \"countries\".* FROM \"countries\" WHERE \"countries\".\"id\" = 10 LIMIT 1\r\nSELECT \"countries\".* FROM \"countries\" WHERE \"countries\".\"id\" = 9 LIMIT 1\r\n<\/pre>\n<p>All vessels of a shipping company are retrieved in a swing with a single database query. The country data sets, however, are retrieved individually. The more countries, the more database queries. Obviously there is potential for improvement.<\/p>\n<h3>Eager loading<\/h3>\n<p>Active Record offers a simple improvement for these situations: the <code>includes <\/code>method. When we request the vessel-objects we are able to tell Rails that we are also interested in the countries-objects:<\/p>\n<pre lang=\"ruby\">@container_vessels = @company.container_vessels.order(:name).\r\n        includes(:legal_country)\r\n<\/pre>\n<p>That was all, no additional changes needed! As a result, these database queries are triggered::<\/p>\n<pre lang=\"sql\">SELECT \"companies\".* FROM \"companies\" \r\n        WHERE \"companies\".\"id\" = ? LIMIT 1 [[\"id\", \"2\"]]\r\nSELECT \"container_vessels\".* FROM \"container_vessels\" \r\n        WHERE \"container_vessels\".\"company_id\" = 2 ORDER BY name\r\nSELECT \"countries\".* FROM \"countries\" \r\n        WHERE \"countries\".\"id\" IN (8, 7, 4)\r\n<\/pre>\n<p>So instead of retrieving each country individually, all countries are loaded in a single query (eager loading). The advantage can be huge for table-heavy information pages.<\/p>\n<p>Unfortunately, there still are some problems:<\/p>\n<ol>\n<li>The query of related records is difficult to customize. For example, one can not simply restrict the data fields with <code>select<\/code>.<\/li>\n<li>The optimization potential is not exhausted. All vessel records are retrieved in one step, in a second step the country data sets are retrieved, and each is identified individually by ID. But SQL should make it possible to get the records together in only query.<\/li>\n<\/ol>\n<p>So there is still enough material for the <a title=\"(Deutsch) Ruby On Rails Database Optimisation Part 2\" href=\"http:\/\/meier-online.com\/2012\/07\/rails_database_optimisation2\/\">continuation of this article<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>Sorry, this entry is only available in Deutsch.<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[104],"tags":[212,114,178,128],"class_list":["post-928","post","type-post","status-publish","format-standard","hentry","category-develop","tag-performance","tag-ruby","tag-scaling","tag-sql","entry"],"_links":{"self":[{"href":"https:\/\/meier-online.com\/en\/wp-json\/wp\/v2\/posts\/928"}],"collection":[{"href":"https:\/\/meier-online.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/meier-online.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/meier-online.com\/en\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/meier-online.com\/en\/wp-json\/wp\/v2\/comments?post=928"}],"version-history":[{"count":33,"href":"https:\/\/meier-online.com\/en\/wp-json\/wp\/v2\/posts\/928\/revisions"}],"predecessor-version":[{"id":2042,"href":"https:\/\/meier-online.com\/en\/wp-json\/wp\/v2\/posts\/928\/revisions\/2042"}],"wp:attachment":[{"href":"https:\/\/meier-online.com\/en\/wp-json\/wp\/v2\/media?parent=928"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/meier-online.com\/en\/wp-json\/wp\/v2\/categories?post=928"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/meier-online.com\/en\/wp-json\/wp\/v2\/tags?post=928"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}