Date post: | 08-Jul-2015 |
Category: |
Technology |
Upload: | leon-fayer |
View: | 407 times |
Download: | 0 times |
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 online:
• @papa_fire
• github:lfayer
• http://fayerplay.com
• https://joind.in/talk/view/11914
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();
$dbh = null;
common database connection
① var $dbh = new DB(…);
② var $sth = $dbh->prepare($query);
① $sth->execute();
② var $result = $sth->fetchAll();
③ $dbh = null;
① 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 = $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’];
}
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();
}
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’];
}
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
)
select userid, email from naughty_users;
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
// 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
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;
why not use transactions?
• no complicated transaction code
• no complicated error handling code
• reduced query overhead
• better performance
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= $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++;
}
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’];
conclusion
learn SQL
other things to consider
1. cache is a wonderful thing
2. * is not your friend
3. EXPLAIN/ANALYZE are
Questions?