Beispiel, wie man eine Rails-Webanwendung durch Optimierung der erzeugten Datenbankabfragen besser skalieren kann.
Vortrag ber der Hamburger Ruby-Usergroup am 8.August 2012.
1. Datenbankoptimierung
Beispiele für die
Optimierung an der
Ruby-on-Rails-Schnittstelle
Karsten Meier
meier-online.com
2. Mein Background
●
1986: SQL im Studium
●
1996: QuarkXpress -> HTML Converter
●
1998-2001: WebObjects, MVC, ORM
●
2004: Erster Kontakt mit Ruby (Pleac)
●
Seit 2005: Handylearn Projects
●
Seit 2009: Nutzung von Rails
2
3. Fallbeispiel Cycosmos
●
Community
●
Webobjects
●
ORM
Enterprise Objects
●
3 Appserver,
1 DB Server
3
6. Fette Objekte
imo
id call_sign build_year
grt
teu
name
speech_of_sponsor draft
machine
imo_certificate
company legal_country
7. Schattenobjekte
ContainerVessel.
select('id, name')
order('name')
●
Read-Only ActiveRecord::ReadOnlyRecord
●
Nur angegebene Attribute
●
Exception falls
unbekannt ActiveRecord::MissingAttributeError
●
ID wirft keine Exception
8. Rosinen picken
●
Nur eine Spalte
●
Objekt unwichtig
●
pluck(column)
●
ab Rails 3.2
ContainerVessel.pluck(:name)
['Australia', 'Brisbane', 'Busan',...]
10. Outsourcing
●
Gewicht aller Container
●
Berechnung kann die DB durchführen
●
Rails sieht die einzelnen Container nicht
@vessel.containers.inject{...}
@vessel.containers.sum('weight')
13. includes()
@container_vessels =
@company.container_vessels.
order(:name).
includes(:legal_country)
SELECT "container_vessels".*
FROM "container_vessels"
WHERE "container_vessels"."company_id" = 2
ORDER BY name
SELECT "countries".*
FROM "countries"
WHERE "countries"."id" IN (8, 7, 4)
17. Rails joins
@container_vessels =
@company.container_vessels.
order(:name).
joins(:legal_country)
●
Keine flaggenlose
Schiffe
●
Keine Staaten
18. Filtern mit joins()
●
Filtern anhand von verbundenen Daten
●
Nur Zielobjekte werden geliefert
●
Vorsicht vor Vervielfachung
@companies = Company.order(:name).
joins(:container_vessels).
where(["container_vessels.build_year > ?", 2009])
SELECT "companies".*
FROM "companies"
INNER JOIN "container_vessels"
ON "container_vessels"."company_id" = "companies"."id"
WHERE (container_vessels.build_year > 2009)
ORDER BY name
19. Automatischer Join
in Associationen
class Country < ActiveRecord::Base
has_many :registering_companies,
:through => :registered_vessels,
:source => 'company',
:class_name => 'Company',
:uniq => true
...
@companies = @country.registering_companies
SELECT DISTINCT "companies".*
FROM "companies"
INNER JOIN "container_vessels"
ON "companies"."id" = "container_vessels"."company_id"
WHERE "container_vessels"."legal_country_id" = 10
25. IDs
●
ID-Vergabe kann
zentraler
Flaschenhals sein
●
IDs schon existent?
26. Transaktionen
●
gewährleisten die Konsistenz (ACID)
●
weniger Sperren, schnelleres Schreiben
●
Ab 2 Schreiboperationen -> nutzen!
27. Massenupdates
●
Firma wird verkauft
●
Alle Schiffen bekommen neuen Besitzer
UPDATE container_vessels
SET company_id = 7
WHERE company_id = 5
connection.update_sql(sql, "Updating vessel...")
28. Verbundene Updates
●
Beispiel Denormalisierung
●
Name des Landes soll auch im
Schiffsdatensatz gespeichert werden
UPDATE container_vessels, country
SET container_vessels.country_name = country.name
WHERE container_vessels.legal_country_id = country.id
29. Keine Angst for SQL
"Many people treat the relational database
like a crazy aunt who's shut up in an attic
and whom nobody wants to talk about"
Martin Fowler: OrmHate
30. ... end
Website von Karsten Meier:
meier-online.com
Bilder:
Container ship by jogdragoon, openclipart.org
Hammer5 by Krystof Jetmar, openclipart.org
OOCL Montreal & Cosco Hope fotografiert von Karsten Meier im Hamburger Hafen 2012