45
High Performance Rails with MySQL Jervin Real, March 2014

High Performance Rails with MySQL

Embed Size (px)

DESCRIPTION

RubyConfPH 2014

Citation preview

Page 1: High Performance Rails with MySQL

High Performance Rails with MySQLJervin Real, March 2014

Page 2: High Performance Rails with MySQL

I am…

• Consultant, Percona

• @dotmanila

• http://dotmanila.com/blog/

• http://www.mysqlperformanceblog.com/

Page 3: High Performance Rails with MySQL

Rails Fu Mastah!

http://walksalong.files.wordpress.com/2007/05/bruce_on_rails.jpg

Page 4: High Performance Rails with MySQL

Beginner

http://www.devonring.ca/img/ouch.png

Page 5: High Performance Rails with MySQL

Customer Problems

Page 6: High Performance Rails with MySQL

Web Apps Performance

• Powerful servers

• CPUs, higher clock speeds

• Lots of memory

• Fast storage

• Scale in the cloud

• Agile development techniques

Page 7: High Performance Rails with MySQL

Why Not?

• Premature scaling is expensive

• Cost inefficient

• Agile means less effective measurement to compensate

for fast deployments

Page 8: High Performance Rails with MySQL

Squeeze the Software

• Exhaust application optimizations first

• You cannot optimize what you can’t measure

• Cacti, NewRelic, Scout

• NewRelic RPM Developer Mode, Rails Footnotes (2, 3,

4!), Google PerfTools for Ruby

Page 9: High Performance Rails with MySQL

• Dumb schemas and queries (somewhat)

Characteristics of Rails

:primary_key, :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean

Page 10: High Performance Rails with MySQL

• Dumb schemas

• Likes to use SHOW FIELDS

Characteristics of Rails

Page 11: High Performance Rails with MySQL

• Dumb schemas

• Likes to use SHOW FIELDS

• N+1 queries problem

• Well documented and discouraged for use

Characteristics of Rails

Page 12: High Performance Rails with MySQL

• Dumb schemas

• Likes to use SHOW FIELDS

• N+1 queries problem

• Well documented and discouraged for use

• Does SELECT FOR UPDATE

• NOOP transactions still wrapped in BEGIN/COMMIT

Characteristics of Rails

Page 13: High Performance Rails with MySQL

• FK relationships - logical - GOOD

Rails - Good

Page 14: High Performance Rails with MySQL

• FK relationships - logical - GOOD

• Knows how to use PRIMARY KEYs — VERY GOOD!

Rails - Good

Page 15: High Performance Rails with MySQL

• FK relationships - logical - GOOD

• Knows how to use PRIMARY KEYs — VERY GOOD!

• Knows NOOP changes - GOOD

Rails - Good

Page 16: High Performance Rails with MySQL

• FK relationships - logical - GOOD

• Knows how to use PRIMARY KEYs — VERY GOOD!

• Knows NOOP changes - GOOD

• Database agnostic - GOOD

Rails - Good

Page 17: High Performance Rails with MySQL

MySQL by Default

• Assumes you have less powerful hardware

• ironically on 5.5, assumes you have very powerful

CPUs - innodb_thread_concurrency = 0

Page 18: High Performance Rails with MySQL

MySQL by Default

• Assumes you have less powerful hardware

• ironically on 5.5, assumes you have very powerful

CPUs - innodb_thread_concurrency = 0

• Not optimized for faster storage

Page 19: High Performance Rails with MySQL

MySQL by Default

• Assumes you have less powerful hardware

• ironically on 5.5, assumes you have very powerful

CPUs - innodb_thread_concurrency = 0

• Not optimized for faster storage

• Still have bad configuration assumptions

• Query cache

• MyISAM < 5.5.5

Page 20: High Performance Rails with MySQL

MySQL by Default

• Assumes you have less powerful hardware

• ironically on 5.5, assumes you have very powerful

CPUs - innodb_thread_concurrency = 0

• Not optimized for faster storage

• Still have bad configuration assumptions

• Query cache

• MyISAM < 5.5.5

• Still haunted by mutexes

Page 21: High Performance Rails with MySQL

What to Optimize - MySQL

• Disable Query Cache

Page 22: High Performance Rails with MySQL

What to Optimize - MySQL

• Disable Query Cache

• query_cache_size = 0

Page 23: High Performance Rails with MySQL

What to Optimize - MySQL

• Disable Query Cache

• query_cache_size = 0

• query_cache_type = 0

Page 24: High Performance Rails with MySQL

What to Optimize - MySQL

• Disable Query Cache

• skip_name_resolve

Page 25: High Performance Rails with MySQL

What to Optimize - MySQL

• Disable Query Cache

• skip_name_resolve

• Use >= 5.5

Page 26: High Performance Rails with MySQL

What to Optimize - MySQL

• Disable Query Cache

• skip_name_resolve

• Use >= 5.5

• Use Indexes, EXPLAIN should be your friend!

Page 27: High Performance Rails with MySQL

What to Optimize - MySQL

• Disable Query Cache

• skip_name_resolve

• Use >= 5.5

• Use Indexes, EXPLAIN should be your friend!

• 5.6 does Subquery Optimizations

Page 28: High Performance Rails with MySQL

What to Optimize - MySQL

• Slow queries - search and destroy

• long_query_time = 0

• log_slow_verbosity - Percona Server

• pt-query-digest/Percona Cloud Tools

Page 29: High Performance Rails with MySQL

What to Optimize - MySQL

• Use InnoDB

innodb_buffer_pool_size       #  keep  hot  data  in  memory  innodb_log_file_size             #  allow  more  IO  buffer  innodb_flush_method  =  O_DIRECT  #  skip  OS  cache  innodb_[read|write]_io_threads  >  4  innodb_io_capacity  innodb_adaptive_flushing_method

Page 30: High Performance Rails with MySQL

What to Optimize - Rails

• counter_cache

• Good for InnoDB if you often SELECT COUNT(*)

• Indexes for find_by, where and family@posts  =  Post.where(title:  params[:keyword])  !mysql>  EXPLAIN  SELECT  `posts`.*  FROM  `posts`    WHERE  `posts`.`title`  =  'LongTitles'  \G  ***************************  1.  row  ***************************                        id:  1      select_type:  SIMPLE                  table:  posts                    type:  ALL  possible_keys:  NULL                      key:  NULL              key_len:  NULL                      ref:  NULL                    rows:  2                  Extra:  Using  where  1  row  in  set  (0.00  sec)

Page 31: High Performance Rails with MySQL

What to Optimize - Rails

• dependent: :destroy

24  Query          BEGIN  24  Query          SELECT  `comments`.*  FROM  `comments`    WHERE  `comments`.`post_id`  =  1  24  Query          DELETE  FROM  `comments`  WHERE  `comments`.`id`  =  2  24  Query          DELETE  FROM  `posts`  WHERE  `posts`.`id`  =  1  24  Query          COMMIT

24  Query          BEGIN  24  Query          DELETE  FROM  `comments`  WHERE  `comments`.`post_id`  =  4  24  Query          DELETE  FROM  `posts`  WHERE  `posts`.`id`  =  4  24  Query          COMMIT

BAD

Use dependent: :delete_all

Page 32: High Performance Rails with MySQL

What to Optimize - Rails

• Cache SHOW FIELDS output - patches for now

• Disable innodb_stats_on_metadata

Page 33: High Performance Rails with MySQL

What to Optimize - Rails

• Cache SHOW FIELDS output - patches for now

• Disable innodb_stats_on_metadata

• Pull only the columns you need - especially excluding

BLOBS

@posts  =  Post.select(“title”)

Page 34: High Performance Rails with MySQL

What to Optimize - Rails

• Avoid pessimistic locks when possible - SELECT … FOR

UPDATE - UPDATE directly and return affected rows

Page 35: High Performance Rails with MySQL

What to Optimize - Rails

• Avoid pessimistic locks when possible - SELECT … FOR

UPDATE - UPDATE directly and return affected rows

• Avoid N+1 queries - use JOIN or includes

Page 36: High Performance Rails with MySQL

What to Optimize - Rails

@posts  =  Post.limit(2)  [email protected]  do  |post|     puts  post.authors.name  end

24  SELECT    `posts`.*  FROM  `posts`    LIMIT  2  24  Query          SELECT    `authors`.*  FROM  `authors`    WHERE  `authors`.`id`  =  1    ORDER  BY  `authors`.`id`  ASC  LIMIT  1  24  Query          SELECT    `authors`.*  FROM  `authors`    WHERE  `authors`.`id`  =  2    ORDER  BY  `authors`.`id`  ASC  LIMIT  1

With this:

You get this:

Page 37: High Performance Rails with MySQL

What to Optimize - Rails

@posts  =  Post.includes(:authors).limit(2)

24  Query          SELECT    `posts`.*  FROM  `posts`    LIMIT  2  24  Query          SELECT  `authors`.*  FROM  `authors`    WHERE  `authors`.`id`  IN  (1,  2)

With this:

You get this:

Page 38: High Performance Rails with MySQL

What to Optimize - Rails

@posts  =  Post.joins(:authors).limit(2);

24  Query          SELECT    `posts`.*  FROM  `posts`  INNER  JOIN  `authors`  ON  `authors`.`id`  =  `posts`.`authors_id`  LIMIT  2

With this:

You get this:

Page 39: High Performance Rails with MySQL

What to Optimize - Rails

• Avoid pessimistic locks when possible - SELECT … FOR

UPDATE - UPDATE directly and return affected rows

• Avoid N+1 queries - use JOIN or includes

• Compress MySQL requests, especially transactions!

Page 40: High Performance Rails with MySQL

What to Optimize - Rails

• Avoid pessimistic locks when possible - SELECT … FOR

UPDATE - UPDATE directly and return affected rows

• Avoid N+1 queries - use JOIN or includes

• Compress MySQL requests, especially transactions!

• Learn and use SQL - find_by_sql

Page 41: High Performance Rails with MySQL

What to Optimize - Rails

• Avoid pessimistic locks when possible - SELECT … FOR

UPDATE - UPDATE directly and return affected rows

• Avoid N+1 queries - use JOIN or includes

• Compress MySQL requests, especially transactions!

• Learn and use SQL - find_by_sql

• Don’t accept Model defaults

Page 42: High Performance Rails with MySQL

Further Thoughts

• GDB, Strace, OProfile

• Smart aggregation, use summary tables when possible

• Rails > Caching > MySQL

• Avoid:config.action_controller.session_store  =  :active_record_store

Page 43: High Performance Rails with MySQL

http://www.amyvernon.net/wp-content/uploads/2014/01/upward-graph-striving.png

Page 44: High Performance Rails with MySQL

Thank you!

Page 45: High Performance Rails with MySQL

… and

• We’re hiring!

• http://www.percona.com/about-us/careers/open-

positions