Larry-<br><br>I'm not claiming to be an expert, but two approaches that we used in 4.6 on SavannahNow.com can really cut db queries.<br><br>I have opened issues for both.<br><br>The first covers path aliasing and allows admins to define the lookup paths that are allowed. In practice, it removes all 'admin' and similar paths from drupal_lookup_path().
<br><br><a href="http://drupal.org/node/75976">http://drupal.org/node/75976</a><br><br>The second is object-caching.&nbsp; The _load functions in Drupal tend to run more often than ideal.&nbsp; Repetitive node_load() calls are lkely due to different modules having to run node_load() to ensure that the node object they need is available,
<br><br>Object caching _might_ help with this.<br><br><a href="http://drupal.org/node/74020">http://drupal.org/node/74020</a><br><br>These are -- I should stress -- both experiments.&nbsp; They need to be tested in large environments (like 
<a href="http://drupal.org">drupal.org</a>, or <a href="http://scratch.drupal.org">scratch.drupal.org</a>) to see if they actually buy us any performance.&nbsp; <div><div><br>- Ken Rickard<br>agentrickard<br>&nbsp;</div><br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Message: 5<br>Date: Fri, 28 Jul 2006 10:49:07 -0500 (CDT)<br>From: &quot;Larry Garfield&quot; &lt;<a href="mailto:larry@garfieldtech.com">larry@garfieldtech.com</a>&gt;<br>Subject: Re: [development] Database queries on <a href="http://drupal.org">
drupal.org</a><br>To: <a href="mailto:development@drupal.org">development@drupal.org</a><br>Message-ID:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;<a href="mailto:1731.70.90.124.113.1154101747.squirrel@webmail.garfieldtech.com">1731.70.90.124.113.1154101747.squirrel@webmail.garfieldtech.com
</a>&gt;<br>Content-Type: text/plain;charset=iso-8859-1<br><br>On Fri, July 28, 2006 9:39 am, Gerhard Killesreiter said:<br><br>&gt; I have now stopped the data collecting.<br>&gt;<br>&gt;&gt; Here are the first queries by accumulated time.
<br><br>&gt; The first column is accumulated time, the second average time/query, the<br>&gt; third the number of times the query was run, the fourth the function<br>&gt; which calls the query, the last the query itself:<br>
<br>&gt; 12585.885&nbsp;&nbsp;&nbsp;&nbsp; 0.01&nbsp;&nbsp;&nbsp;&nbsp;1322443 cache_get&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT data, created, headers, expire<br>&gt; FROM cache WHERE cid = S<br>&gt; 7524.278&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.001&nbsp;&nbsp; 6814856 drupal_lookup_path&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT dst FROM url_alias<br>&gt; WHERE src = S
<br>&gt; 6062.889&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.107&nbsp;&nbsp; 56508&nbsp;&nbsp; theme_forum_topic_navigation&nbsp;&nbsp;&nbsp;&nbsp;SELECT n.nid, n.title,<br>&gt; n.sticky, l.comment_count, l.last_comment_timestamp FROM node n INNER<br>&gt; JOIN node_comment_statistics l ON n.nid = 
l.nid INNER JOIN term_node r<br>&gt; ON n.nid = r.nid AND r.tid = D<br>&gt; 4343.435&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.024&nbsp;&nbsp; 183059&nbsp;&nbsp;node_load&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT n.nid, n.vid, n.type, n.status,<br>&gt; n.created, n.changed, n.comment, n.promote, n.moderate
, n.sticky,<br>&gt; r.timestamp AS revision_timestamp, r.title, r.body, r.teaser, r.log,<br>&gt; r.format, u.uid, <a href="http://u.name">u.name</a>, u.picture, u.data FROM node n INNER JOIN users<br>&gt; u ON u.uid = n.uid
 INNER JOIN node_revisions r ON r.vid = n.vid WHERE<br>&gt; n.nid = D<br>&gt; 1837.531&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.04&nbsp;&nbsp;&nbsp;&nbsp;45429&nbsp;&nbsp; node_load&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT n.nid, n.vid, n.type, n.status,<br>&gt; n.created, n.changed, n.comment, n.promote, n.moderate
, n.sticky,<br>&gt; r.timestamp AS revision_timestamp, r.title, r.body, r.teaser, r.log,<br>&gt; r.format, u.uid, <a href="http://u.name">u.name</a>, u.picture, u.data FROM node n INNER JOIN users<br>&gt; u ON u.uid = n.uid
 INNER JOIN node_revisions r ON r.vid = n.vid WHERE<br>&gt; n.nid = S<br>&gt; 1538.485&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.011&nbsp;&nbsp; 143889&nbsp;&nbsp;cache_set&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INSERT INTO cache (cid, data, created,<br>&gt; expire, headers) VALUES (S)<br><br><br>&gt;&gt; Here are the first few queries by average query time, columns as above.
<br><br>&gt; 1343.749&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3.262&nbsp;&nbsp; 412&nbsp;&nbsp;&nbsp;&nbsp; pager_query&nbsp;&nbsp;&nbsp;&nbsp; SELECT DISTINCT(n.nid), n.title, n.type,<br>&gt; n.changed, n.uid, <a href="http://u.name">u.name</a>, l.last_comment_timestamp AS last_post,<br>&gt; l.comment_count
 FROM node n INNER JOIN users u ON n.uid = u.uid INNER<br>&gt; JOIN node_comment_statistics l ON n.nid = l.nid WHERE n.status = D<br>&gt; 1045.001&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.969&nbsp;&nbsp; 352&nbsp;&nbsp;&nbsp;&nbsp; pager_query&nbsp;&nbsp;&nbsp;&nbsp; SELECT DISTINCT(n.nid), n.title, n.type
,<br>&gt; n.changed, n.uid, <a href="http://u.name">u.name</a>, l.last_comment_timestamp AS last_post,<br>&gt; l.comment_count FROM node n INNER JOIN node_comment_statistics l ON<br>&gt; n.nid = l.nid INNER JOIN users u ON 
n.uid = u.uid LEFT JOIN comments c<br>&gt; ON n.nid = c.nid AND (c.status = D<br>&gt; 628.091&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.784&nbsp;&nbsp; 352&nbsp;&nbsp;&nbsp;&nbsp; pager_query&nbsp;&nbsp;&nbsp;&nbsp; SELECT COUNT(DISTINCT(n.nid)) FROM node n<br>&gt; LEFT JOIN comments c ON n.nid = c.nid AND (
c.status = D<br>&gt; 1091.909&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.327&nbsp;&nbsp; 823&nbsp;&nbsp;&nbsp;&nbsp; pager_query&nbsp;&nbsp;&nbsp;&nbsp; SELECT DISTINCT m.*, r.*, <a href="http://u.name">u.name</a>, u.uid<br>&gt; FROM cvs_messages m INNER JOIN cvs_repositories r ON m.rid = r.rid INNER<br>&gt; JOIN users u ON 
m.uid = u.uid ORDER BY m.created DESC LIMIT D<br>&gt; 19.668&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.093&nbsp;&nbsp; 18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;eval&nbsp;&nbsp;&nbsp;&nbsp;select message, count(message) as count from<br>&gt; watchdog where type = S and uid = D<br>&gt; 147.989&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.08&nbsp;&nbsp;&nbsp;&nbsp;137&nbsp;&nbsp;&nbsp;&nbsp; cache_get&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DELETE FROM cache WHERE expire != D
<br>&gt; 1.03&nbsp;&nbsp;1.03&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pager_query&nbsp;&nbsp;&nbsp;&nbsp; SELECT a.aid, a.timestamp, a.url, a.uid, <a href="http://u.name">u.name</a><br>&gt; FROM accesslog a LEFT JOIN users u ON a.uid = u.uid WHERE a.path LIKE S<br>&gt; ORDER BY a.timestamp
 DESC LIMIT D<br>&gt; 10.087&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.56&nbsp;&nbsp;&nbsp;&nbsp;18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;eval&nbsp;&nbsp;&nbsp;&nbsp;select message, count(message) as count from<br>&gt; watchdog where type = S and uid != D<br>&gt; 136.43&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.493&nbsp;&nbsp; 277&nbsp;&nbsp;&nbsp;&nbsp; pager_query&nbsp;&nbsp;&nbsp;&nbsp; SELECT COUNT(DISTINCT(
n.nid)) FROM node n<br>&gt; INNER JOIN project_issues p ON p.nid = n.nid INNER JOIN node_revisions r<br>&gt; ON r.vid = n.vid INNER JOIN users u ON p.assigned = u.uid LEFT JOIN<br>&gt; project_comments c ON c.nid = p.nid WHERE 
n.status = D<br>&gt; 132.688&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.479&nbsp;&nbsp; 277&nbsp;&nbsp;&nbsp;&nbsp; pager_query&nbsp;&nbsp;&nbsp;&nbsp; SELECT DISTINCT(n.nid) FROM node n INNER<br>&gt; JOIN project_issues p ON p.nid = n.nid INNER JOIN node_revisions r ON<br>&gt; r.vid = n.vid INNER JOIN users u ON 
p.assigned = u.uid LEFT JOIN<br>&gt; project_comments c ON c.nid = p.nid WHERE n.status = D<br>&gt; 1247.877&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.473&nbsp;&nbsp; 2640&nbsp;&nbsp;&nbsp;&nbsp;do_search&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CREATE TEMPORARY TABLE temp_search_sids<br>&gt; SELECT i.type, i.sid, SUM(i.score
 * t.count) AS relevance, COUNT(*) AS<br>&gt; matches FROM search_index i INNER JOIN search_total t ON i.word = t.word<br>&gt; INNER JOIN node n ON n.nid = i.sid INNER JOIN users u ON n.uid = u.uid<br>&gt; WHERE n.status = D
<br>&gt; 0.933 0.466&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aggregator_page_sources SELECT f.fid, f.title,<br>&gt; f.description, f.image, MAX(i.timestamp) AS last FROM aggregator_feed f<br>&gt; LEFT JOIN aggregator_item i ON f.fid = i.fid GROUP BY f.fid
, f.title,<br>&gt; f.description, f.image ORDER BY last DESC, f.title<br>&gt; 0.429 0.429&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pager_query&nbsp;&nbsp;&nbsp;&nbsp; SELECT q.qid, q.query, q.function, t.*,<br>&gt; COUNT(t.qid) AS count, SUM(t.time) AS total_time, AVG(t.time
) AS<br>&gt; average, STDDEV(t.time) AS stddev FROM devel_queries q INNER JOIN<br>&gt; devel_times t ON q.qid = t.qid GROUP BY t.qid ORDER BY total_time DESC<br>&gt; LIMIT D<br>&gt; 9.767 0.425&nbsp;&nbsp; 23&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_search&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CREATE TEMPORARY TABLE temp_search_sids SELECT
<br>&gt; i.type, i.sid, SUM(i.score * t.count) AS relevance, COUNT(*) AS matches<br>&gt; FROM search_index i INNER JOIN search_total t ON i.word = t.word INNER<br>&gt; JOIN node n ON n.nid = i.sid INNER JOIN term_node tn ON 
n.nid = tn.nid<br>&gt; INNER JOIN users u ON n.uid = u.uid WHERE n.status = D<br>&gt; 6.014 0.401&nbsp;&nbsp; 15&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pager_query&nbsp;&nbsp;&nbsp;&nbsp; SELECT uid, access FROM users WHERE uid &gt; D<br>&gt; 0.372 0.372&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pager_query&nbsp;&nbsp;&nbsp;&nbsp; SELECT COUNT(*) FROM accesslog a LEFT JOIN
<br>&gt; users u ON a.uid = u.uid WHERE a.path LIKE S<br>&gt; 96.123&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.364&nbsp;&nbsp; 264&nbsp;&nbsp;&nbsp;&nbsp; forum_get_forums&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT r.tid, COUNT(n.nid) AS<br>&gt; topic_count, SUM(l.comment_count) AS comment_count FROM node n INNER
<br>&gt; JOIN node_comment_statistics l ON n.nid = l.nid INNER JOIN term_node r<br>&gt; ON n.nid = r.nid WHERE n.status = D<br>&gt; 42.274&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.282&nbsp;&nbsp; 150&nbsp;&nbsp;&nbsp;&nbsp; cache_clear_all DELETE FROM cache WHERE cid = S<br><br><br>
To my admittedly non-expert eye, this suggests a few (probably obvious,<br>but I'll say them anyway) observations:<br><br>- The cache system and path alias system are far and away the most<br>database-intensive systems in Drupal, by over an order of magnitude.&nbsp;&nbsp;In
<br>both cases, though, it's more due to frequency of call than to complexity.<br> Simply reducing the number of queries should help more than trying to<br>simplify them.<br><br>- Someone needs to get that query out of the forum module's theme code. :-)
<br><br>- Node loading is also db-intensive (who'd a thunk it, right?)<br><br>- If node_load()'s main query is called that often, I wonder what else<br>it's doing in the same routine?&nbsp;&nbsp;A given node is loaded by several<br>
queries, which would probably make the function more expensive than it<br>seems from these logs.<br><br>- Pagers are expensive, which I don't think is much of a surprise.<br><br>- Flushing the cache is non-cheap.&nbsp;&nbsp;I recall a while back someone
<br>mentioning MySQL's &quot;deferred&quot; functionality, which could be useful here.<br>(If it takes an extra half-second for a cache-flush to commit, that's<br>probably not a problem for the business logic.)<br><br>I'm sure some other database experts will have more insightful commentary.
<br>:-)<br><br>--Larry Garfield<br><br><br><br></blockquote></div><br>