Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
PHP Performance 101:
so you need to use a database
Leon Fayer
@papa_fire
Who am I ?
• 20+ years development and operations of web applications
• currently Vice President at OmniTI
• can be found ...
what it is about
databases & performance
what it’s not about
NoSQL MySQL
how database connection works
① establish connection
② send query
③ process query
④ send result
⑤ close connection
common database connection
$dbh = new DB(…);
$sth = $dbh->prepare($query);
$sth->execute();
$result = $sth->fetchAll();
$d...
common database connection
① var $dbh = new DB(…);
② var $sth = $dbh->prepare($query);
① $sth->execute();
② var $result = ...
① establish connection
① establish connection
⑤ close connection
and
problem
connection overhead
visual representation
①
①
①
⑤
⑤
⑤
short answer
persistent connections
short and helpful answer
persistent connections
avoid multiple connections
how it works (high level)
①
①
①
⑤
⑤
⑤
①
⑤
conclusion
reduce # of connections
② send query
most common problem
n+1
n+1
// get a list of items
$sth = $dbh->prepare("select item_id from items
where active = true");
$sth->execute();
$items ...
n+1 you don’t know about
// get a list of items
$items = get_active_item_ids();
// get properties for each items
foreach (...
easy solution
// get a list of items with properties
$sth = $dbh->prepare("select i. item_id, p.* from items i,
item_prope...
conclusion
limit number of queries
cool stuff
:BONUS:
Common Table Expressions
(CTEs)
* MySQL does not support CTEs
// create temp table naughty_users
// and get data from it
with naughty_users as (
select * from users where banned = 1
)
...
even more cool
Writeable
Common Table Expressions
* Postgres 9.1+ only
multiple queries are required
// create user record
insert into users (name, email) values (?,?) returning
userid
// creat...
or are they?
with userdata as (
insert into users (name, email) values (?,?)
returning userid
), addressdata as (
insert i...
why not use transactions?
• no complicated transaction code
• no complicated error handling code
• reduced query overhead
...
find out more
For more details:
http://omniti.com/seeds/writable-ctes-improve-performance
③ process query
unpopular opinion
ORMs are evil
why?
1. machine-generated
2. object construction overhead
3. false sense of control
in one sentence
you have no idea how it works
timely tweet
conclusion
learn SQL
④ send results
may be shocking, but …
databases can do math
illustrating wrong
// get all orders
$sth = $dbh->prepare("select order_id, price from orders");
$sth->execute();
$orders=...
vs right
// get average $ for last 10 orders
$sth = $dbh->prepare("select avg(price) as avg_price
from (select price from ...
conclusion
learn SQL
other things to consider
1. cache is a wonderful thing
2. * is not your friend
3. EXPLAIN/ANALYZE are
Questions?
Upcoming SlideShare
Loading in …5
×

PHP performance 101: so you need to use a database

1,256 views

Published on

Being involved in performance audits on systems of every size, from start-up sites hacked together overnight, to a ginormous applications built by world-recognized brand companies, I’ve seen a lot of interesting (and sometimes very unique) performance issues in every level of the stack: code, architecture, databases (sometimes all of the above). But there are a few particular, very “Performance 101″, issues that (unfortunately) appear in a lot of code bases. In this talk I present the most common database-related performance bottlenecks that can happen in most PHP applications.

Published in: Technology
  • Be the first to comment

PHP performance 101: so you need to use a database

  1. 1. PHP Performance 101: so you need to use a database Leon Fayer @papa_fire
  2. 2. Who am I ? • 20+ years development and operations of web applications • currently Vice President at OmniTI • can be found online: • @papa_fire • github:lfayer • http://fayerplay.com • https://joind.in/talk/view/11914
  3. 3. what it is about databases & performance
  4. 4. what it’s not about NoSQL MySQL
  5. 5. how database connection works ① establish connection ② send query ③ process query ④ send result ⑤ close connection
  6. 6. common database connection $dbh = new DB(…); $sth = $dbh->prepare($query); $sth->execute(); $result = $sth->fetchAll(); $dbh = null;
  7. 7. common database connection ① var $dbh = new DB(…); ② var $sth = $dbh->prepare($query); ① $sth->execute(); ② var $result = $sth->fetchAll(); ③ $dbh = null;
  8. 8. ① establish connection
  9. 9. ① establish connection ⑤ close connection and
  10. 10. problem connection overhead
  11. 11. visual representation ① ① ① ⑤ ⑤ ⑤
  12. 12. short answer persistent connections
  13. 13. short and helpful answer persistent connections avoid multiple connections
  14. 14. how it works (high level) ① ① ① ⑤ ⑤ ⑤ ① ⑤
  15. 15. conclusion reduce # of connections
  16. 16. ② send query
  17. 17. most common problem n+1
  18. 18. n+1 // get a list of items $sth = $dbh->prepare("select item_id from items where active = true"); $sth->execute(); $items = $sth->fetchAll(); // get properties for each items foreach ($items as $i) { $sth_prop = $dbh->prepare("select * from item_properties where item_id = ?"); $sth_prop->execute($item); $item_list[$i[‘item_id’]][‘props’] = $sth_prop->fetchAll(); $item_list[$i[‘item_id’]][‘id’] = $i[‘id’]; }
  19. 19. n+1 you don’t know about // get a list of items $items = get_active_item_ids(); // get properties for each items foreach ($items as $i) { $item = Item->new($i[‘item_id’]) $item_list[$i[‘item_id’][‘id’] = $item->item_id; $item_list[$i[‘item_id’]][‘props’] = $item->properties(); }
  20. 20. easy solution // get a list of items with properties $sth = $dbh->prepare("select i. item_id, p.* from items i, item_properties p where i.item_id = p.item_id and active = true"); $sth->execute(); $items = $sth->fetchAll(); // arrange object to your liking foreach ($items as $i) { $item_list[$i[‘item_id’]][‘props’] = $i; $item_list[$i[‘item_id’]][‘id’] = $i[‘id’]; }
  21. 21. conclusion limit number of queries
  22. 22. cool stuff :BONUS: Common Table Expressions (CTEs) * MySQL does not support CTEs
  23. 23. // create temp table naughty_users // and get data from it with naughty_users as ( select * from users where banned = 1 ) select userid, email from naughty_users;
  24. 24. even more cool Writeable Common Table Expressions * Postgres 9.1+ only
  25. 25. multiple queries are required // create user record insert into users (name, email) values (?,?) returning userid // create address record insert into addresses (userid, address, city, state, zip) values (?,?,?,?,?) returning addressid // track changes to user information insert into user_history (userid, addressid, action) values (?,?,?) returning historyid
  26. 26. or are they? with userdata as ( insert into users (name, email) values (?,?) returning userid ), addressdata as ( insert into addresses (userid, address, city, state, zip) select userid,?,?,?,? from userdata returning addressid ), historydata as ( insert into user_history (userid, addressid, action) select userid, addressid,? from userdata, addressdata returning historyid ) select userid, addressid, historyid from userdata, addressdata, historydata;
  27. 27. why not use transactions? • no complicated transaction code • no complicated error handling code • reduced query overhead • better performance
  28. 28. find out more For more details: http://omniti.com/seeds/writable-ctes-improve-performance
  29. 29. ③ process query
  30. 30. unpopular opinion ORMs are evil
  31. 31. why? 1. machine-generated 2. object construction overhead 3. false sense of control
  32. 32. in one sentence you have no idea how it works
  33. 33. timely tweet
  34. 34. conclusion learn SQL
  35. 35. ④ send results
  36. 36. may be shocking, but … databases can do math
  37. 37. illustrating wrong // get all orders $sth = $dbh->prepare("select order_id, price from orders"); $sth->execute(); $orders= $sth->fetchAll(); //order by order_id usort($orders, function ($a, $b) { if ($a['order_id'] == $b['order_id']) { return 0; } return $a['order_id'] < $b['order_id'] ? -1 : 1; }); // get average $ for last 10 orders $count = 1; $total = 0; $avg = 0; foreach ($orders as $order) { $total += $order[‘price’]; if ($count == 10) { $avg = $total/$count; break 1; } $count++; }
  38. 38. vs right // get average $ for last 10 orders $sth = $dbh->prepare("select avg(price) as avg_price from (select price from orders order by order_id desc limit 10) "); $sth->execute(); $orders= $sth->fetchAll(); $avg = $orders[‘avg_price’];
  39. 39. conclusion learn SQL
  40. 40. other things to consider 1. cache is a wonderful thing 2. * is not your friend 3. EXPLAIN/ANALYZE are
  41. 41. Questions?

×