datenbankoptimierung für ruby on rails

30
Datenbankoptimierung Karsten Meier meier-online.com Beispiele für die Optimierung an der Ruby-on-Rails-Schnittstelle

Upload: karsten-meier

Post on 27-Jul-2015

362 views

Category:

Documents


0 download

TRANSCRIPT

Datenbankoptimierung

Karsten Meiermeier-online.com

Beispiele für die Optimierung an der

Ruby-on-Rails-Schnittstelle

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

3

Fallbeispiel Cycosmos

● Community● Webobjects● ORM

Enterprise Objects● 3 Appserver,

1 DB Server

Reduktion der Datenbankzugriffe

● Bessere Antwortzeiten

● Weniger Datenbanklast

● 300% höherer Durchsatz

● Höhere Stabilität

Die Schichten einer Anwendung

Fette Objekte

id

draft

name

teubuild_year

legal_countrycompany

call_signimo

imo_certificate

speech_of_sponsor

grt

machine

Schattenobjekte

ContainerVessel.select('id, name')order('name')

● Read-Only● Nur angegebene Attribute● Exception falls

unbekannt● ID wirft keine Exception

ActiveRecord::MissingAttributeError

ActiveRecord::ReadOnlyRecord

Rosinen picken

● Nur eine Spalte● Objekt unwichtig● pluck(column) ● ab Rails 3.2

ContainerVessel.pluck(:name)['Australia', 'Brisbane', 'Busan',...]

Darf das Schiff ablegen?

Outsourcing

● Gewicht aller Container● Berechnung kann die DB durchführen● Rails sieht die einzelnen Container nicht

@vessel.containers.inject{...}

@vessel.containers.sum('weight')

Verknüpfte Objekte

● Eine Reederei mit Liste ihrer Schiffe und Flaggenstaaten

Ablaufdiagramm

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)

includes()

● Jede Abfrage liefert einen Objekttyp

● Rails behält Kontrolle

● Schachtelung möglich

● Feintuning schwierig

.includes(:legal_country => :tax_rates)

.select('country.image????')

Was war noch mal ein Join?

Inner/Left/Outer/Right

Rails joins

● Keine flaggenlose Schiffe

● Keine Staaten

@container_vessels = @company.container_vessels.

order(:name).joins(:legal_country)

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

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

Join direkt verwenden?

Echte Datenbankjoins aus Rails

connection = Company.connection

columns = "container_vessels.id, container_vessels.name,\ container_vessels.imo, container_vessels.teu, \

countries.name as legal_country_name"

sql = 'SELECT ' + columns + ' FROM "container_vessels" \ JOIN "countries" \ ON "countries"."id" = "container_vessels"."legal_country_id" \ WHERE "container_vessels"."company_id" = ' + @company.id.to_s + ' ORDER BY "container_vessels".name'

@vessel_data = connection.select_all(sql, 'ContainerVessel Overview Load')

select_all Rückgabewerte

● select_all: array of hashes● select_rows: array of arrays

<% @vessel_data.each do |data| %> <tr> <td><%= data['name'] %></td> <td><%= data['imo'] %></td> <td><%= data['teu'] %></td> <td><%= data['legal_country_name'] %></td>

...<% end %>

Parameter-Überprüfung

● SQL-Injection● Methoden leider

etwas versteckt● Ab Rails 3.2:

ActiveRecord::Sanitization

● Bei IDs: to_i.to_str

Company.where('name like '%?', input)

record.sanitize_sql_array(..)

replace_bind_variables()quote_bound_value()

connection.quote_string()

Schreiben

Wenn es Performanceprobleme beim Schreiben gibt, dann sind sie meistens schwerwiegend.

IDs

● ID-Vergabe kann zentraler Flaschenhals sein

● IDs schon existent?

Transaktionen

● gewährleisten die Konsistenz (ACID)● weniger Sperren, schnelleres Schreiben● Ab 2 Schreiboperationen -> nutzen!

Massenupdates

UPDATE container_vesselsSET company_id = 7WHERE company_id = 5

● Firma wird verkauft● Alle Schiffen bekommen neuen Besitzer

connection.update_sql(sql, "Updating vessel...")

Verbundene Updates

● Beispiel Denormalisierung● Name des Landes soll auch im

Schiffsdatensatz gespeichert werden

UPDATE container_vessels, countrySET container_vessels.country_name = country.nameWHERE container_vessels.legal_country_id = country.id

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

... end

meier-online.com

Website von Karsten Meier:

Bilder: Container ship by jogdragoon, openclipart.orgHammer5 by Krystof Jetmar, openclipart.orgOOCL Montreal & Cosco Hope fotografiert von Karsten Meier im Hamburger Hafen 2012