<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ars Longa, Vita Brevis &#187; MySQL</title>
	<atom:link href="http://blog.sjinks.pro/tag/mysql/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.sjinks.pro</link>
	<description>Quod scripsi, scripsi</description>
	<lastBuildDate>Mon, 06 Feb 2012 17:56:02 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Вложенные транзакции в PDO</title>
		<link>http://blog.sjinks.pro/php/932-pdo-nested-transactions/</link>
		<comments>http://blog.sjinks.pro/php/932-pdo-nested-transactions/#comments</comments>
		<pubDate>Sat, 06 Aug 2011 07:21:34 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PDO]]></category>
		<category><![CDATA[PostgreSQL]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=932</guid>
		<description><![CDATA[Использование вложенных транзакций в MySQL и PostgreSQL с PDO В PDO есть такая особенность: отсутствие поддержки вложенных транзакций. Вложенные транзакции бывают весьма полезны: например, у нас есть два сервиса, которые могут взаимодействовать между собой. Сервис А начинает транзакцию, что-то делает, затем вызывает сервис Б, затем подтверждает транзакцию. Может так случиться, что сервисы А и Б разрабатываются разными людьми и разработчик [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/932-pdo-nested-transactions/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Использование вложенных транзакций в <a href="http://blog.sjinks.pro/tag/mysql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  MySQL">MySQL</a> и <a href="http://blog.sjinks.pro/tag/postgresql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PostgreSQL">PostgreSQL</a> с <a href="http://blog.sjinks.pro/tag/pdo/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PDO">PDO</a></em></h2>
<p>В PDO есть такая особенность: отсутствие поддержки вложенных транзакций.</p>
<p>Вложенные транзакции бывают весьма полезны: например, у нас есть два сервиса, которые могут взаимодействовать между собой. Сервис А начинает транзакцию, что-то делает, затем вызывает сервис Б, затем подтверждает транзакцию.</p>
<p>Может так случиться, что сервисы А и Б разрабатываются разными людьми и разработчик сервиса Б может не знать, что сервис А уже начал выполнение транзакции. Как следствие, при использовании PDO как только сервис Б попытается начать транзакцию, PDO выбросит исключение, предупреждающее о наличии активной транзакции.</p>
<p>Тем не менее, такие СУБД как MySQL и PostgreSQL поддерживают вложенные транзакции (о чём PDO не знает). Поэтому было бы неплохо добавить поддержку вложенных транзакций для данных СУБД в PDO.<span id="more-932"></span></p>
<p>Ниже приведена очень простая реализация поддержки вложенных транзакций в PDO:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p9322">
        <div class="code php" id="p932code2">
<span class="kw2">class</span> MyPDO <span class="kw2">extends</span> PDO<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw2">protected</span> <span class="kw2">static</span> <span class="re0">$_supported_drivers</span> <span class="sy0">=</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'mysql'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'pgsql'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span><br />
&nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">protected</span> <span class="re0">$_nest_level</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">protected</span> <span class="kw2">function</span> nestedTransactionsSupported<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="sy0">!</span><span class="kw1">empty</span><span class="br0">&#40;</span><span class="kw2">self</span><span class="sy0">::</span><span class="re0">$_supported_drivers</span><span class="br0">&#91;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getAttribute</span><span class="br0">&#40;</span>PDO<span class="sy0">::</span><span class="me2">ATTR_DRIVER_NAME</span><span class="br0">&#41;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> beginTransaction<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_nest_level <span class="sy0">||</span> <span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">nestedTransactionsSupported</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">parent</span><span class="sy0">::</span><span class="me2">beginTransaction</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">exec</span><span class="br0">&#40;</span><span class="st0">&quot;SAVEPOINT LEVEL<span class="es4">{$this-&gt;_nest_level}</span>&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">++</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_nest_level<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> commit<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">--</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_nest_level<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_nest_level <span class="sy0">||</span> <span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">nestedTransactionsSupported</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">parent</span><span class="sy0">::</span><span class="me2">commit</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">exec</span><span class="br0">&#40;</span><span class="st0">&quot;RELEASE SAVEPOINT LEVEL<span class="es4">{$this-&gt;_nest_level}</span>&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> rollBack<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">--</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_nest_level<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_nest_level <span class="sy0">||</span> <span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">nestedTransactionsSupported</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">parent</span><span class="sy0">::</span><span class="me2">rollBack</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">exec</span><span class="br0">&#40;</span><span class="st0">&quot;ROLLBACK TO SAVEPOINT LEVEL<span class="es4">{$this-&gt;_nest_level}</span>&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p><strong>Подводные камни:</strong> в MySQL есть такое понятие как <dfn>неявное подтверждение транзакции</dfn> (<a href="http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html">implicit commit</a>). На практике это ломает уровень вложенности транзакций, поддерживаемый данной реализацией. С другой стороны, в web-приложениях редко возникает необходимость использования транзакций (тем более вложенных) там, где присутствуют операторы, вызывающие неявное подтверждение транзакции.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/932-pdo-nested-transactions/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/932-pdo-nested-transactions/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Блокировки транзакций InnoDB при удалении данных из таблицы</title>
		<link>http://blog.sjinks.pro/mysql/889-lock-wait-timeout-exceeded-try-restarting-transaction/</link>
		<comments>http://blog.sjinks.pro/mysql/889-lock-wait-timeout-exceeded-try-restarting-transaction/#comments</comments>
		<pubDate>Sat, 05 Feb 2011 16:01:16 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[оптимизация]]></category>
		<category><![CDATA[производительность]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=889</guid>
		<description><![CDATA[Lock wait timeout exceeded; try restarting transaction Ситуация: есть несколько физических почтовых серверов (PowerMTA), отсылающих более четырёх миллионов сообщений в сутки. Есть виртуальный сервер базы данных (причём не очень мощный), на котором крутится MySQL с InnoDB; в базу данных пишутся логи доставки/не доставки сообщений и ведётся статистика по IP-адресам, VMTA и доменам. Попутно выполняется классификация и [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/mysql/889-lock-wait-timeout-exceeded-try-restarting-transaction/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Lock wait timeout exceeded; try restarting transaction</em></h2>
<p>Ситуация: есть несколько физических почтовых серверов (PowerMTA), отсылающих более четырёх миллионов сообщений в сутки. Есть виртуальный сервер базы данных (причём не очень мощный), на котором крутится <a href="http://blog.sjinks.pro/tag/mysql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  MySQL">MySQL</a> с <a href="http://blog.sjinks.pro/tag/innodb/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  InnoDB">InnoDB</a>; в базу данных пишутся логи доставки/не доставки сообщений и ведётся статистика по IP-адресам, VMTA и доменам. Попутно выполняется классификация и анализ hard и soft bounces. База за день увеличивается примерно на 5 гигабайт. Часть логов недельной давности удаляется.<span id="more-889"></span></p>
<p>Так как с базой (на запись) работает несколько клиентов, структура таблиц/индексы выбраны таким образом, чтобы обеспечить максимальную скорость добавления записей и минимум блокировок.</p>
<p>Проблема возникла с ведением статистики.</p>
<p>Есть таблица такой структуры:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p8896">
        <div class="code mysql" id="p889code6">
<span class="kw1">CREATE</span> <span class="kw1">TABLE</span> <span class="kw15">log</span> <span class="br0">&#40;</span><br />
&nbsp; &nbsp; id <span class="kw4">BIGINT</span> <span class="kw6">UNSIGNED</span> <span class="kw10">NOT</span> <span class="kw3">NULL</span> <span class="kw1">PRIMARY KEY</span> <span class="kw6">AUTO_INCREMENT</span><span class="sy2">,</span><br />
&nbsp; &nbsp; ymd <span class="kw4">INTEGER</span> <span class="kw10">NOT</span> <span class="kw3">NULL</span><span class="sy2">,</span><br />
&nbsp; &nbsp; domain <span class="kw4">VARCHAR</span><span class="br0">&#40;</span>255<span class="br0">&#41;</span> <span class="kw10">NOT</span> <span class="kw3">NULL</span><span class="sy2">,</span><br />
&nbsp; &nbsp; vmta <span class="kw4">VARCHAR</span><span class="br0">&#40;</span>255<span class="br0">&#41;</span> <span class="kw10">NOT</span> <span class="kw3">NULL</span><span class="sy2">,</span><br />
&nbsp; &nbsp; ip <span class="kw4">VARCHAR</span><span class="br0">&#40;</span>45<span class="br0">&#41;</span> <span class="kw10">NOT</span> <span class="kw3">NULL</span><span class="sy2">,</span><br />
&nbsp; &nbsp; deliveries <span class="kw4">TINYINT</span> <span class="kw6">UNSIGNED</span> <span class="kw10">NOT</span> <span class="kw3">NULL</span><span class="sy2">,</span><br />
&nbsp; &nbsp; bounces <span class="kw4">TINYINT</span> <span class="kw6">UNSIGNED</span> <span class="kw10">NOT</span> <span class="kw3">NULL</span><span class="sy2">,</span><br />
&nbsp; &nbsp; transients <span class="kw4">TINYINT</span> <span class="kw6">UNSIGNED</span> <span class="kw10">NOT</span> <span class="kw3">NULL</span><span class="sy2">,</span><br />
&nbsp; &nbsp; pmta_bounces <span class="kw4">TINYINT</span> <span class="kw6">UNSIGNED</span> <span class="kw10">NOT</span> <span class="kw3">NULL</span><br />
<span class="br0">&#41;</span> <span class="kw1">ENGINE</span><span class="sy1">=</span><span class="kw1">InnoDB</span> <span class="kw2">DEFAULT</span> <span class="kw7">CHARSET</span><span class="sy1">=</span>utf8<span class="sy2">;</span>
        </div>
    </div>
</div>

<p>В таблицу заносятся данные о доставке сообщений — выполняется это триггерами post insert: при доставке сообщения в таблицу заносится запись <span class="codebox"><code class="mysql"><span class="br0">&#40;</span><span class="kw3">NULL</span><span class="sy2">,</span> <span class="kw17">DATE_FORMAT</span><span class="br0">&#40;</span><span class="kw17">NOW</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy2">,</span> <span class="st0">&quot;<span class="es1">%</span>Y<span class="es1">%</span>m<span class="es1">%</span>d&quot;</span><span class="br0">&#41;</span><span class="sy2">,</span> domain<span class="sy2">,</span> vmta<span class="sy2">,</span> ip<span class="sy2">,</span> <span class="nu0">1</span><span class="sy2">,</span> <span class="nu0">0</span><span class="sy2">,</span> <span class="nu0">0</span><span class="sy2">,</span> <span class="nu0">0</span><span class="sy2">,</span> <span class="nu0">0</span><span class="br0">&#41;</span></code></span>, при получении hard bounce — <span class="codebox"><code class="mysql"><span class="br0">&#40;</span><span class="kw3">NULL</span><span class="sy2">,</span> <span class="kw17">DATE_FORMAT</span><span class="br0">&#40;</span><span class="kw17">NOW</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy2">,</span> <span class="st0">&quot;<span class="es1">%</span>Y<span class="es1">%</span>m<span class="es1">%</span>d&quot;</span><span class="br0">&#41;</span><span class="sy2">,</span> domain<span class="sy2">,</span> vmta<span class="sy2">,</span> ip<span class="sy2">,</span> <span class="nu0">0</span><span class="sy2">,</span> <span class="nu0">1</span><span class="sy2">,</span> <span class="nu0">0</span><span class="sy2">,</span> <span class="nu0">0</span><span class="sy2">,</span> <span class="nu0">0</span><span class="br0">&#41;</span></code></span> — всё это выполняется довольно быстро, жалоб нет.</p>
<p>Проблемы начинаются при обработке данного лога. Что нужно сделать: подсчитать SUM(deliveries), SUM(bounces), SUM(transients), SUM(pmta_bounces) для каждой четвёрки (ymd, domain, vmta, ip). Данная операция выполняется каждые десять минут. Обработанные данные из таблицы log нужно удалить.</p>
<p>В терминах SQL это выглядит примерно так:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p8897">
        <div class="code mysql" id="p889code7">
<span class="kw1">CREATE</span> <span class="kw1">PROCEDURE</span> populate_stats_daily<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
<span class="kw1">BEGIN</span><br />
&nbsp; &nbsp; <span class="kw1">DECLARE</span> max_id <span class="kw4">BIGINT</span> <span class="kw6">UNSIGNED</span><span class="sy2">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">START</span> <span class="kw1">TRANSACTION</span><span class="sy2">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">SELECT</span> <span class="kw22">MAX</span><span class="br0">&#40;</span>id<span class="br0">&#41;</span> <span class="kw1">INTO</span> max_id <span class="kw1">FROM</span> <span class="kw15">log</span><span class="sy2">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">INSERT</span> <span class="kw1">INTO</span> stats_daily <span class="br0">&#40;</span>ymd<span class="sy2">,</span> domain<span class="sy2">,</span> vmta<span class="sy2">,</span> ip<span class="sy2">,</span> deliveries<span class="sy2">,</span> bounces<span class="sy2">,</span> transients<span class="sy2">,</span> pmta_bounces<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">SELECT</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ymd<span class="sy2">,</span> domain<span class="sy2">,</span> vmta<span class="sy2">,</span> ip<span class="sy2">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw22">SUM</span><span class="br0">&#40;</span>deliveries<span class="br0">&#41;</span> <span class="kw1">AS</span> deliveries<span class="sy2">,</span> <span class="kw22">SUM</span><span class="br0">&#40;</span>bounces<span class="br0">&#41;</span> <span class="kw1">AS</span> bounces<span class="sy2">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw22">SUM</span><span class="br0">&#40;</span>transients<span class="br0">&#41;</span> <span class="kw1">AS</span> transients<span class="sy2">,</span> <span class="kw22">SUM</span><span class="br0">&#40;</span>pmta_bounces<span class="br0">&#41;</span> <span class="kw1">AS</span> pmta_bounces<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FROM</span> <span class="kw15">log</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">WHERE</span> id <span class="sy1">&lt;=</span> max_id<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">GROUP BY</span> ymd<span class="sy2">,</span> domain<span class="sy2">,</span> vmta<span class="sy2">,</span> ip<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ORDER BY</span> <span class="kw3">NULL</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ON</span> <span class="kw1">DUPLICATE KEY</span> <span class="kw1">UPDATE</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; deliveries &nbsp; <span class="sy1">=</span> deliveries <span class="sy1">+</span> <span class="kw1">VALUES</span><span class="br0">&#40;</span>deliveries<span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bounces &nbsp; &nbsp; &nbsp;<span class="sy1">=</span> bounces <span class="sy1">+</span> <span class="kw1">VALUES</span><span class="br0">&#40;</span>bounces<span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; transients &nbsp; <span class="sy1">=</span> transients <span class="sy1">+</span> <span class="kw1">VALUES</span><span class="br0">&#40;</span>transients<span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pmta_bounces <span class="sy1">=</span> pmta_bounces <span class="sy1">+</span> <span class="kw1">VALUES</span><span class="br0">&#40;</span>pmta_bounces<span class="br0">&#41;</span><span class="sy2">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">DELETE</span> <span class="kw1">FROM</span> <span class="kw15">log</span> <span class="kw1">WHERE</span> id <span class="sy1">&lt;=</span> max_id<span class="sy2">;</span><br />
&nbsp; &nbsp; <span class="kw1">COMMIT</span><span class="sy2">;</span><br />
<span class="kw12">END</span>
        </div>
    </div>
</div>

<p>Проблема наступает при выполнении оператора <code>DELETE</code>: <code>SHOW PROCESSLIST</code> показывает состояние <code>Updating</code> минуты полторы, после чего в лог пишется печально известное сообщение <strong>Lock wait timeout exceeded; try restarting transaction</strong> и транзакция откатывается. При этом количество записей в таблице <code>log</code> всего лишь порядка 80,000.</p>
<p>В принципе, проблема с <code>DELETE</code> <a href="http://www.google.ru/search?q=InnoDB+slow+delete">давно известна</a>: в InnoDB удаление строк весьма тормозное. По всей видимости, это усугубляется тем, что в таблицу постоянно идёт запись новых данных. Руководство пользователя <a href="http://dev.mysql.com/doc/refman/5.1/en/innodb-locks-set.html">объясняет</a> суть проблемы.</p>
<p>А решение таково: заводим дополнительную колонку в таблице (<code>processed</code>) и создаём по ней индекс. <code>DELETE FROM log WHERE id <= max_id</code> заменяем на <code>UPDATE log SET processed = 1 WHERE id <= max_id</code>, что выполняется гораздо быстрее. Затем заводим еще одну транзакцию, в которой удаляем строки с <code>processed = 1</code>.</p>
<p>Но в один заход все строки удалить не получится, поэтому придётся идти на хитрость:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p8898">
        <div class="code mysql" id="p889code8">
<span class="kw1">BEGIN</span><br />
&nbsp; &nbsp; <span class="kw1">DECLARE</span> cnt <span class="kw4">BIGINT</span> <span class="kw6">UNSIGNED</span><span class="sy2">;</span><br />
<br />
<span class="coMULTI">/* ... */</span><br />
<br />
&nbsp; &nbsp; <span class="kw13">REPEAT</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">START</span> <span class="kw1">TRANSACTION</span><span class="sy2">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">DELETE</span> <span class="kw1">FROM</span> <span class="kw15">log</span> <span class="kw1">WHERE</span> processed <span class="sy1">=</span> 1 <span class="kw1">LIMIT</span> <span class="nu0">1000</span><span class="sy2">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">SELECT</span> <span class="kw23">ROW_COUNT</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="kw1">INTO</span> cnt<span class="sy2">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">COMMIT</span><span class="sy2">;</span><br />
&nbsp; &nbsp; UNTIL cnt <span class="sy1">=</span> 0 <span class="kw12">END</span> <span class="kw13">REPEAT</span><span class="sy2">;</span><br />
<span class="kw12">END</span>
        </div>
    </div>
</div>

<p>Смысл в том, что удаление производится небольшими кусками (что будет относительно быстро); каждое удаление выполняется в своей транзакции (в случае блокировки транзакции в хвосте таблицы большая часть записей будет удалена, что не так плохо).</p>
<p>Вполне возможно, что можно обойтись без дополнительной колонки и использовать <code>REPEAT … END REPEAT</code> с <code>DELETE FROM log WHERE id <= max_id</code>, но текущий вариант работает, кушать не просит, блокировок не создаёт. А наше основное правило — <span title="if it ain't broke, don't fix it">si fractum non sit, noli id reficere</span>.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/mysql/889-lock-wait-timeout-exceeded-try-restarting-transaction/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/mysql/889-lock-wait-timeout-exceeded-try-restarting-transaction/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Дельта-бэкапы баз данных</title>
		<link>http://blog.sjinks.pro/linux/829-database-delta-backups/</link>
		<comments>http://blog.sjinks.pro/linux/829-database-delta-backups/#comments</comments>
		<pubDate>Tue, 28 Sep 2010 09:45:21 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[база данных]]></category>
		<category><![CDATA[резервная копия]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=829</guid>
		<description><![CDATA[Разностное резервное копирование экономит дисковое пространство На одном из серверов с большим числом пользователей у нас настроено автоматическое создание резервных копий баз данных — четыре раза в день. Стоит отметить, что размер сжатых резервных копий всех баз данных превышает 400 мегабайт. Таким образом, за один день на резервные копии баз данных уходит около двух гигабайт дискового пространства. [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/linux/829-database-delta-backups/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Разностное резервное копирование экономит дисковое пространство</em></h2>
<p>На одном из серверов с большим числом пользователей у нас настроено автоматическое создание резервных копий баз данных — четыре раза в день. Стоит отметить, что размер сжатых резервных копий всех баз данных превышает 400 мегабайт. Таким образом, за один день на резервные копии баз данных уходит около двух гигабайт дискового пространства. Две недели — почти 30 гигабайт. Расточительство.</p>
<p>Для экономии места мы изобрели велосипед под названием «дельта-бэкап» (известный как <dfn>разностный бэкап</dfn>, <a href="http://en.wikipedia.org/wiki/Incremental_backup#Differential">differential backup</a>) — мы храним одну полную резервную копию базы данных и список изменений.</p>
<p>Ввиду того, что клиентские сайты используют <a href="http://blog.sjinks.pro/tag/wordpress/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  WordPress">WordPress</a>, разностное резервное копирование баз данных позволяет сэкономить очень много дискового пространства.<span id="more-829"></span></p>
<p>Идея состоит в том, что недельные изменения в базе данных (<dfn>дельта</dfn>) гораздо меньше по размеру, чем полный дамп базы данных. В случае с блогом обычно так и есть, а в случае с форумом — как повезёт.</p>
<p>Привожу одно из возможных решений, возможно, что кому-то оно пригодится.</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p82910">
        <div class="code bash" id="p829code10">
<span class="co0">#! /bin/sh</span><br />
<br />
<span class="re2">BACKUPDIR</span>=<span class="sy0">/</span>var<span class="sy0">/</span>backups<span class="sy0">/</span>mysql<br />
<span class="re2">DATE</span>=<span class="sy0">`</span><span class="kw2">date</span> +<span class="sy0">%</span>y<span class="sy0">%</span>W<span class="sy0">%</span>u-<span class="sy0">%</span>y<span class="sy0">%</span>m<span class="sy0">%</span>d-<span class="sy0">%</span>H<span class="sy0">%</span>M<span class="sy0">%</span>S<span class="sy0">`</span><br />
<span class="re2">DATE2</span>=<span class="sy0">`</span><span class="kw2">date</span> +<span class="sy0">%</span>y<span class="sy0">%</span>W<span class="sy0">`</span><br />
<span class="re2">MYSQL_USER</span>=<span class="st0">&quot;user&quot;</span><br />
<span class="re2">MYSQL_PASS</span>=<span class="st0">&quot;pass&quot;</span><br />
<span class="re2">MYSQLDUMP</span>=<span class="sy0">/</span>usr<span class="sy0">/</span>bin<span class="sy0">/</span>mysqldump <span class="re5">--quote-names</span> <span class="re5">--skip-lock-tables</span> <span class="re5">--skip-comments</span> <span class="re5">--skip-extended-insert</span> -u<span class="re1">$MYSQL_USER</span> -p<span class="re1">$MYSQL_PASS</span><br />
<br />
<span class="kw3">umask</span> 077<br />
<span class="re2">databases</span>=<span class="sy0">`</span>mysql -u<span class="re1">$MYSQL_USER</span> -p<span class="re1">$MYSQL_PASS</span> <span class="re5">-B</span> <span class="re5">-N</span> <span class="re5">-e</span> <span class="st_h">'SHOW DATABASES'</span> <span class="sy0">|</span> <span class="kw2">xargs</span><span class="sy0">`</span><br />
<span class="kw1">for</span> db <span class="kw1">in</span> <span class="re1">$databases</span>; <span class="kw1">do</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#91;</span> <span class="st0">&quot;<span class="es2">$db</span>&quot;</span> <span class="sy0">!</span>= <span class="st0">&quot;information_schema&quot;</span> <span class="br0">&#93;</span>; <span class="kw1">then</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">echo</span> <span class="st0">&quot;Backing up <span class="es3">${db}</span>...&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">$MYSQLDUMP</span> <span class="st0">&quot;<span class="es2">$db</span>&quot;</span> <span class="sy0">|</span> <span class="kw2">gzip</span> <span class="re5">-5</span> <span class="sy0">&gt;</span> <span class="co1">${BACKUPDIR}</span><span class="sy0">/</span><span class="co1">${db}</span>-<span class="co1">${DATE}</span>.sql.gz<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re2">files</span>=<span class="sy0">`</span><span class="kw3">echo</span> <span class="re1">$BACKUPDIR</span><span class="sy0">/</span><span class="re1">$db</span>-<span class="re1">$DATE2</span><span class="sy0">*</span>.sql.gz<span class="sy0">`</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re2">base</span>=<span class="st0">&quot;&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> j <span class="kw1">in</span> <span class="re1">$files</span>; <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#91;</span> <span class="st0">&quot;<span class="es2">$base</span>&quot;</span> = <span class="st0">&quot;&quot;</span> <span class="br0">&#93;</span>; <span class="kw1">then</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re2">base</span>=<span class="re1">$j</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re2">fname</span>=<span class="sy0">`</span><span class="kw2">basename</span> <span class="st0">&quot;<span class="es2">$j</span>&quot;</span> .sql.gz<span class="sy0">`</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">zdiff</span> <span class="re5">-U1</span> <span class="re5">-d</span> <span class="re1">$base</span> <span class="re1">$j</span> <span class="sy0">|</span> <span class="kw2">bzip2</span> <span class="re5">-c</span> <span class="re5">-9</span> <span class="sy0">&gt;</span> <span class="re1">$BACKUPDIR</span><span class="sy0">/</span><span class="re1">$fname</span>.diff.bz2<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">rm</span> <span class="re5">-f</span> <span class="re1">$j</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">fi</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">done</span><br />
&nbsp; &nbsp; <span class="kw1">fi</span><br />
<span class="kw1">done</span>
        </div>
    </div>
</div>

<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/linux/829-database-delta-backups/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/linux/829-database-delta-backups/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Небуферизованные запросы: снижаем потребление памяти WordPress</title>
		<link>http://blog.sjinks.pro/wordpress/813-unbuffered-queries-lowering-worpress-memory-requirements/</link>
		<comments>http://blog.sjinks.pro/wordpress/813-unbuffered-queries-lowering-worpress-memory-requirements/#comments</comments>
		<pubDate>Thu, 02 Sep 2010 17:48:38 +0000</pubDate>
		<dc:creator>Wandering Soul</dc:creator>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[база данных]]></category>
		<category><![CDATA[оптимизация]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=813</guid>
		<description><![CDATA[Снижение пикового потребления памяти благодаря в два раза заменой одной функции Пиковое потребление оперативной памяти WordPress можно снизить приблизительно два раза и практически бесплатно. В чём секрет? В использовании правильных функций для работы с базой данных. Опытные программисты знают, что API MySQL предоставляет два варианта работы с результатом запроса: Последовательная обработка результата — при этом не происходит [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/wordpress/813-unbuffered-queries-lowering-worpress-memory-requirements/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Снижение пикового потребления памяти благодаря в два раза заменой одной функции</em></h2>
<p>Пиковое потребление оперативной памяти <a href="http://blog.sjinks.pro/tag/wordpress/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  WordPress">WordPress</a> можно снизить приблизительно два раза и практически бесплатно. В чём секрет? В использовании правильных функций для работы с базой данных.</p>
<p>Опытные программисты знают, что API <a href="http://blog.sjinks.pro/tag/mysql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  MySQL">MySQL</a> предоставляет два варианта работы с результатом запроса:</p>
<ol>
<li>Последовательная обработка результата — при этом не происходит никакой буферизации результата, данные отдаются от сервера клиенту, минуя временные таблицы и буфера. В этом случае обработка результатов может производиться только последовательно.</li>
<li>Буферирование результата запроса — результат полностью читается во временный буфер, что позволяет осуществлять произвольный доступ к результату.</li>
</ol>
<p><span id="more-813"></span></p>
<p>Первому варианту соответствует использование функции <a href="http://blog.sjinks.pro/tag/php/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PHP">PHP</a> <a href="http://php.net/manual/en/function.mysql-unbuffered-query.php" rel="nofollow"><code>mysql_unbuffered_query()</code></a>, второму — <a href="http://php.net/manual/en/function.mysql-query.php" rel="nofollow"><code>mysql_query()</code></a>.</p>
<p>WordPress использует именно второй вариант. Но специфика функции <span class="codebox"><code class="php">wpdb<span class="sy0">::</span><span class="me2">query</span><span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> такова, что, выполнив запрос, WordPress полностью читает ответ в переменную <span class="codebox"><code class="php">wpdb<span class="sy0">::</span><span class="me2">last_result</span></code></span>.</p>
<p>Таким образом, при выполнении запроса, возвращающего большой объём данных:</p>
<ol>
<li>MySQL выделит память для временного буфера под <em>весь результат запроса</em></li>
<li>WordPress создаст локальную копию результата запроса</li>
<li>После вызова <code>mysql_result_free()</code> временный буфер будет освобождён</li>
</ol>
<p>В результате связка WordPress + MySQL съест в два раза больше памяти, чем нужно. Это особенно ощутимо на системах с небольшим количеством доступной оперативной памяти.</p>
<p><strong>Лечение:</strong> в файле <code>wp-includes/wp-db.php</code> заменяем <code>mysql_query</code> на <code>mysql_unbuffered_query</code>.</p>
<p><strong>Из бонусов</strong>: при использовании <span class="codebox"><code class="php"><span class="kw3">mysql_unbuffered_query</span><span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> PHP будет получать данные по мере их доступности (во время выполнения запроса), в то время как при использовании <code>mysql_query()</code> пришлось бы ждать окончания выполнения запроса.</p>
<p>Использование небуферизованных запросов, <a href="http://www.moskalyuk.com/blog/mysql_unbuffered_query-faster-than-mysql_query/1318">судя по отзывам</a>, очень сильно позволяет <a href="http://www.tuxradar.com/practicalphp/9/4/9">повысить производительность</a>.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/wordpress/813-unbuffered-queries-lowering-worpress-memory-requirements/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/wordpress/813-unbuffered-queries-lowering-worpress-memory-requirements/feed/</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
		<item>
		<title>Исправление ошибки в INSERT INTO … SELECT в Kohana 3 при использовании конфигурации базы данных, отличной от default</title>
		<link>http://blog.sjinks.pro/php/kohana/806-fix-insert-select-bug-with-non-default-db-config-in-kohana-3/</link>
		<comments>http://blog.sjinks.pro/php/kohana/806-fix-insert-select-bug-with-non-default-db-config-in-kohana-3/#comments</comments>
		<pubDate>Thu, 12 Aug 2010 07:40:55 +0000</pubDate>
		<dc:creator>Wandering Soul</dc:creator>
				<category><![CDATA[Kohana]]></category>
		<category><![CDATA[Kohana 3]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[база данных]]></category>
		<category><![CDATA[ошибка]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=806</guid>
		<description><![CDATA[Исправление короче описания Ситуация: нужно выполнить запрос вида INSERT INTO `table` (`col1`, `col2`) SELECT * FROM `table` WHERE `col1` &#60;&#62; 0; Если использовать конфигурацию default, то всё отлично работает. Если же использовать другую конфигурацию, то можем получить ошибку соединения с базой данных и исключение. Например: &#60;?php // application/config/database.php // Важно, чтобы в конфигурации default нельзя [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/kohana/806-fix-insert-select-bug-with-non-default-db-config-in-kohana-3/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Исправление короче описания</em></h2>
<p><strong>Ситуация:</strong> нужно выполнить запрос вида</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80617">
        <div class="code sql" id="p806code17">
<span class="kw1">INSERT</span> <span class="kw1">INTO</span> <span class="st0">`table`</span> <span class="br0">&#40;</span><span class="st0">`col1`</span><span class="sy0">,</span> <span class="st0">`col2`</span><span class="br0">&#41;</span> <span class="kw1">SELECT</span> <span class="sy0">*</span> <span class="kw1">FROM</span> <span class="st0">`table`</span> <span class="kw1">WHERE</span> <span class="st0">`col1`</span> <span class="sy0">&lt;&gt;</span> <span class="nu0">0</span>;
        </div>
    </div>
</div>

<p>Если использовать конфигурацию <code>default</code>, то всё отлично работает. Если же использовать другую конфигурацию, то можем получить ошибку соединения с базой данных и исключение.<span id="more-806"></span></p>
<p>Например:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80618">
        <div class="code php" id="p806code18">
<span class="kw2">&lt;?php</span><br />
<span class="co1">// application/config/database.php</span><br />
<span class="co1">// Важно, чтобы в конфигурации default нельзя было подключиться к серверу</span><br />
<span class="kw1">return</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; <span class="st_h">'test'</span> <span class="sy0">=&gt;</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'type'</span> &nbsp; &nbsp; &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'mysql'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'connection'</span> <span class="sy0">=&gt;</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'hostname'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'localhost'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'database'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'test'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'username'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'username'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'password'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'password'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'persistent'</span> <span class="sy0">=&gt;</span> <span class="kw4">FALSE</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'table_prefix'</span> <span class="sy0">=&gt;</span> <span class="st_h">''</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'charset'</span> &nbsp; &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="st_h">'utf8'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'caching'</span> &nbsp; &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw4">FALSE</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'profiling'</span> &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw4">TRUE</span><span class="sy0">,</span><br />
&nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">,</span><br />
<span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="sy1">?&gt;</span><br />
<br />
<span class="kw2">&lt;?php</span><br />
<span class="co1">// application/classes/controller/welcome.php</span><br />
<br />
<span class="kw2">class</span> Controller_Welcome <span class="kw2">extends</span> Controller<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> action_index<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">request</span><span class="sy0">-&gt;</span><span class="me1">response</span> <span class="sy0">=</span> DB<span class="sy0">::</span><span class="me2">insert</span><span class="br0">&#40;</span><span class="st_h">'test'</span><span class="sy0">,</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="st_h">'col1'</span><span class="sy0">,</span> <span class="st_h">'col2'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">select</span><span class="br0">&#40;</span>DB<span class="sy0">::</span><span class="me2">select</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">from</span><span class="br0">&#40;</span><span class="st_h">'test'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">where</span><span class="br0">&#40;</span><span class="st_h">'column1'</span><span class="sy0">,</span> <span class="st_h">'&lt;&gt;'</span><span class="sy0">,</span> <span class="st_h">'0'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">compile</span><span class="br0">&#40;</span>Database<span class="sy0">::</span><span class="me2">instance</span><span class="br0">&#40;</span><span class="st_h">'test'</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy0">.</span> <span class="st0">&quot;<span class="es1">\n</span>&quot;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="sy1">?&gt;</span>
        </div>
    </div>
</div>

<p>Важно, чтобы правая часть условия в <code>where()</code> была строкой.</p>
<p>В результате если мы запустим проект, то получим что-то вида:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80619">
        <div class="code text" id="p806code19">
INSERT INTO `test` (`column1`, `column2`) ErrorException [ 2 ]: mysql_connect(): Access denied for user 'www-data'@'localhost' (using password: NO) ~ MODPATH/database/classes/kohana/database/mysql.php [ 56 ]
        </div>
    </div>
</div>

<p>Связано это с особенностью реализации метода <code><a href="http://blog.sjinks.pro/tag/kohana/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana">Kohana</a>_Database_Query_Builder_Insert::compile()</code>:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80620">
        <div class="code php" id="p806code20">
&nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> compile<span class="br0">&#40;</span>Database <span class="re0">$db</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Start an insertion query</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">=</span> <span class="st_h">'INSERT INTO '</span><span class="sy0">.</span><span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">quote_table</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_table<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Не интересно</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// …</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">is_array</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_values<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Не интересно</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// …</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">'VALUES '</span><span class="sy0">.</span><span class="kw3">implode</span><span class="br0">&#40;</span><span class="st_h">', '</span><span class="sy0">,</span> <span class="re0">$groups</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Add the sub-query</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="br0">&#40;</span>string<span class="br0">&#41;</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_values<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$query</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Строка <span class="codebox"><code class="php"><span class="re0">$query</span> <span class="sy0">.=</span> <span class="br0">&#40;</span>string<span class="br0">&#41;</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_values<span class="sy0">;</span></code></span> вызывает метод <code>compile()</code> для <code>SELECT</code>, но так как конфигурация базы данных не передаётся, используется конфигурация <code>default</code>.</p>
<p>Самое простое исправление очевидно: заменить строку</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80621">
        <div class="code php" id="p806code21">
<span class="re0">$query</span> <span class="sy0">.=</span> <span class="br0">&#40;</span>string<span class="br0">&#41;</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_values<span class="sy0">;</span>
        </div>
    </div>
</div>

<p>на </p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80622">
        <div class="code php" id="p806code22">
<span class="re0">$query</span> <span class="sy0">.=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_values<span class="sy0">-&gt;</span><span class="me1">compile</span><span class="br0">&#40;</span><span class="re0">$db</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Тем не менее, если <code>SELECT</code> использует одну базу данных, а <code>INSERT</code> — другую, и при этом используются разные charset&#8217;ы (да, это не самое большое извращение — бывают и хуже), этого будет недостаточно. В этом случае нужно добавить дополнительный параметр в метод <code>select()</code>, указывающий, какую конфигурацию использовать. Требуемые изменения кода тривиальны, посему остаются упражнением интересующемуся читателю.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/kohana/806-fix-insert-select-bug-with-non-default-db-config-in-kohana-3/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/kohana/806-fix-insert-select-bug-with-non-default-db-config-in-kohana-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Поддержка JOIN в запросах DELETE для Kohana 3</title>
		<link>http://blog.sjinks.pro/php/kohana/805-join-support-for-delete-in-kohana-3/</link>
		<comments>http://blog.sjinks.pro/php/kohana/805-join-support-for-delete-in-kohana-3/#comments</comments>
		<pubDate>Wed, 11 Aug 2010 10:48:13 +0000</pubDate>
		<dc:creator>Wandering Soul</dc:creator>
				<category><![CDATA[Kohana]]></category>
		<category><![CDATA[Kohana 3]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[база данных]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=805</guid>
		<description><![CDATA[Добавление поддержки multiple-table DELETE в Query Builder Kohana 3 Иногда бывает полезно выполнить удаление записей в нескольких связанных таблицах, благо синтаксис позволяет. Разумеется, есть такая вещь как каскадное удаление записей, но в случае с MySQL (а именно — таблицами MyISAM) на неё надеяться не приходится. Модуль Database из Kohana 3 не умеет использовать JOIN с операцией DELETE. Нужно [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/kohana/805-join-support-for-delete-in-kohana-3/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Добавление поддержки multiple-table DELETE в Query Builder <a href="http://blog.sjinks.pro/tag/kohana/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana">Kohana</a> 3</em></h2>
<p>Иногда бывает полезно выполнить удаление записей в нескольких связанных таблицах, благо <a href="http://dev.mysql.com/doc/refman/5.0/en/delete.html">синтаксис позволяет</a>. Разумеется, есть такая вещь как каскадное удаление записей, но в случае с <a href="http://blog.sjinks.pro/tag/mysql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  MySQL">MySQL</a> (а именно — таблицами MyISAM) на неё надеяться не приходится.</p>
<p>Модуль Database из <a href="http://blog.sjinks.pro/tag/kohana-3/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana 3">Kohana 3</a> не умеет использовать JOIN с операцией DELETE. Нужно ему помочь.<span id="more-805"></span></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80528">
        <div class="code php" id="p805code28">
<span class="kw2">&lt;?php</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">class</span> Database_Query_Builder_Delete <span class="kw2">extends</span> Kohana_Database_Query_Builder_Delete<br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">protected</span> <span class="re0">$_to_delete</span> <span class="sy0">=</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">protected</span> <span class="re0">$_join</span> <span class="sy0">=</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">protected</span> <span class="re0">$_last_join</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co4">/**<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Adds addition tables to &quot;JOIN ...&quot;.<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param mixed &nbsp; column name or array($column, $alias) or object<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param string &nbsp;join type (LEFT, RIGHT, INNER, etc)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @return Database_Query_Builder_Delete<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> <span class="kw3">join</span><span class="br0">&#40;</span><span class="re0">$table</span><span class="sy0">,</span> <span class="re0">$type</span> <span class="sy0">=</span> <span class="kw4">NULL</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span>_join<span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_last_join <span class="sy0">=</span> <span class="kw2">new</span> Database_Query_Builder_Join<span class="br0">&#40;</span><span class="re0">$table</span><span class="sy0">,</span> <span class="re0">$type</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> delete_table<span class="br0">&#40;</span><span class="re0">$table</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span>_to_delete<span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$table</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co4">/**<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Adds &quot;ON ...&quot; conditions for the last created JOIN statement.<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param &nbsp; mixed &nbsp; column name or array($column, $alias) or object<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param &nbsp; string &nbsp;logic operator<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param &nbsp; mixed &nbsp; column name or array($column, $alias) or object<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @return &nbsp;$this<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> on<span class="br0">&#40;</span><span class="re0">$c1</span><span class="sy0">,</span> <span class="re0">$op</span><span class="sy0">,</span> <span class="re0">$c2</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span>_last_join<span class="sy0">-&gt;</span><span class="me1">on</span><span class="br0">&#40;</span><span class="re0">$c1</span><span class="sy0">,</span> <span class="re0">$op</span><span class="sy0">,</span> <span class="re0">$c2</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co4">/**<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Compile the SQL query and return it.<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param Database &nbsp;Database instance<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @return string<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> compile<span class="br0">&#40;</span>Database <span class="re0">$db</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">=</span> <span class="st_h">'DELETE'</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_to_delete<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">' '</span> <span class="sy0">.</span> <span class="kw3">join</span><span class="br0">&#40;</span><span class="st_h">', '</span><span class="sy0">,</span> <span class="kw3">array_map</span><span class="br0">&#40;</span><span class="kw1">array</span><span class="br0">&#40;</span><span class="re0">$db</span><span class="sy0">,</span> <span class="st_h">'quote_identifier'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_to_delete<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">' FROM '</span> <span class="sy0">.</span> <span class="re0">$db</span><span class="sy0">-&gt;</span><span class="me1">quote_table</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_table<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_join<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">' '</span> <span class="sy0">.</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_compile_join<span class="br0">&#40;</span><span class="re0">$db</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_join<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_where<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">' WHERE '</span> <span class="sy0">.</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_compile_conditions<span class="br0">&#40;</span><span class="re0">$db</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_where<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_join<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_order_by<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">' '</span> <span class="sy0">.</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_compile_order_by<span class="br0">&#40;</span><span class="re0">$db</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_order_by<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span>_limit <span class="sy0">!==</span> <span class="kw4">null</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$query</span> <span class="sy0">.=</span> <span class="st_h">' LIMIT '</span> <span class="sy0">.</span> <span class="re0">$this</span><span class="sy0">-&gt;</span>_limit<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw <span class="kw2">new</span> Kohana_Exception<span class="br0">&#40;</span><span class="st_h">'Cannot use ORDER BY or LIMIT in a multiple-table DELETE'</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$query</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">public</span> <span class="kw2">function</span> <span class="kw3">reset</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">parent</span><span class="sy0">::</span><span class="kw3">reset</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span>_to_delete <span class="sy0">=</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span>_join &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span>_last_join <span class="sy0">=</span> <span class="kw4">null</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
<span class="sy1">?&gt;</span>
        </div>
    </div>
</div>

<p>Данный класс добавляет функциональность, связанную с удалением данных из нескольких таблиц, в Query Builder. Использование покажу на примерах:</p>
<p>Запросу</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80529">
        <div class="code mysql" id="p805code29">
<span class="kw1">DELETE</span> t1<span class="sy2">,</span> t2 <span class="kw1">FROM</span> t1 <span class="kw1">INNER</span> <span class="kw1">JOIN</span> t2 <span class="kw1">INNER</span> <span class="kw1">JOIN</span> t3<br />
<span class="kw1">WHERE</span> t1.id<span class="sy1">=</span>t2.id <span class="kw10">AND</span> t2.id<span class="sy1">=</span>t3.id<span class="sy2">;</span>
        </div>
    </div>
</div>

<p>соответствует код</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80530">
        <div class="code php" id="p805code30">
DB<span class="sy0">::</span><span class="me2">delete</span><span class="br0">&#40;</span><span class="st_h">'t1'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">join</span><span class="br0">&#40;</span><span class="st_h">'t2'</span><span class="sy0">,</span> <span class="st_h">'INNER'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">on</span><span class="br0">&#40;</span><span class="st_h">'t1.id'</span><span class="sy0">,</span> <span class="st_h">'='</span><span class="sy0">,</span> <span class="st_h">'t2.id'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">join</span><span class="br0">&#40;</span><span class="st_h">'t3'</span><span class="sy0">,</span> <span class="st_h">'INNER'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">on</span><span class="br0">&#40;</span><span class="st_h">'t2.id'</span><span class="sy0">,</span> <span class="st_h">'='</span><span class="sy0">,</span> <span class="st_h">'t3.id'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">delete_table</span><span class="br0">&#40;</span><span class="st_h">'t1'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">delete_table</span><span class="br0">&#40;</span><span class="st_h">'t2'</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Запросу </p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80531">
        <div class="code mysql" id="p805code31">
<span class="kw1">DELETE</span> <span class="kw1">FROM</span> t1 <span class="kw1">INNER</span> <span class="kw1">JOIN</span> t2 <span class="kw1">INNER</span> <span class="kw1">JOIN</span> t3<br />
<span class="kw1">WHERE</span> t1.id<span class="sy1">=</span>t2.id <span class="kw10">AND</span> t2.id<span class="sy1">=</span>t3.id<span class="sy2">;</span>
        </div>
    </div>
</div>

<p>соответствует код</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80532">
        <div class="code php" id="p805code32">
DB<span class="sy0">::</span><span class="me2">delete</span><span class="br0">&#40;</span><span class="st_h">'t1'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">join</span><span class="br0">&#40;</span><span class="st_h">'t2'</span><span class="sy0">,</span> <span class="st_h">'INNER'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">on</span><span class="br0">&#40;</span><span class="st_h">'t1.id'</span><span class="sy0">,</span> <span class="st_h">'='</span><span class="sy0">,</span> <span class="st_h">'t2.id'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">join</span><span class="br0">&#40;</span><span class="st_h">'t3'</span><span class="sy0">,</span> <span class="st_h">'INNER'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">-&gt;</span><span class="me1">on</span><span class="br0">&#40;</span><span class="st_h">'t2.id'</span><span class="sy0">,</span> <span class="st_h">'='</span><span class="sy0">,</span> <span class="st_h">'t3.id'</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Как видим, метод <code>Database_Query_Builder_Delete::delete_table()</code> задаёт имена таблиц, которые нужно удалить; если данный метод не вызывать, будут удалены <strong>все</strong> таблицы, перечисленные в запросе.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/kohana/805-join-support-for-delete-in-kohana-3/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/kohana/805-join-support-for-delete-in-kohana-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQLMon для Code Igniter</title>
		<link>http://blog.sjinks.pro/php/769-sqlmon-for-code-igniter/</link>
		<comments>http://blog.sjinks.pro/php/769-sqlmon-for-code-igniter/#comments</comments>
		<pubDate>Sat, 13 Feb 2010 06:10:58 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Code Igniter]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[SQLMon]]></category>
		<category><![CDATA[оптимизация]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=769</guid>
		<description><![CDATA[Мониторинг SQL-запросов в Code Igniter На днях мне довелось заняться оптимизацией одного проекта, написанного с использованием фреймворка Code Igniter. Важной частью процесса являлась оптимизация запросов к базе данных, а для этого как минимум нужно видеть эти запросы перед глазами. Этот минимум в Code Igniter достигается очень просто: вызовом $this-&#62;output-&#62;enable_profiler(TRUE); в контроллере. Но когда запросов на [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/769-sqlmon-for-code-igniter/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Мониторинг SQL-запросов в <a href="http://blog.sjinks.pro/tag/code-igniter/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Code Igniter">Code Igniter</a></em></h2>
<p>На днях мне довелось заняться оптимизацией одного проекта, написанного с использованием фреймворка Code Igniter. Важной частью процесса являлась <a href="http://blog.sjinks.pro/tag/optimization/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  оптимизация">оптимизация</a> запросов к базе данных, а для этого как минимум нужно видеть эти запросы перед глазами.</p>
<p>Этот минимум в Code Igniter достигается очень просто: вызовом</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p76936">
        <div class="code php" id="p769code36">
<span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">output</span><span class="sy0">-&gt;</span><span class="me1">enable_profiler</span><span class="br0">&#40;</span><span class="kw4">TRUE</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>в контроллере. Но когда запросов на странице очень много, хотелось бы избавиться от рутинного выполнения <span class="codebox"><code class="mysql"><span class="kw1">EXPLAIN</span></code></span> для каждого подозрительного запроса. Что, собственно, и делает <a href="http://blog.sjinks.pro/tag/sqlmon/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  SQLMon">SQLMon</a>. Возникает логичное желание интегрировать его в Code Igniter.<span id="more-769"></span></p>
<p>Но в отличие от <a href="http://blog.sjinks.pro/kohana/762-sqlmon-for-kohana-3/">Kohana</a>, с Code Igniter здесь не всё так просто. Во-первых, Code Igniter не поддерживает так называемую <dfn><a href="http://v3.kohanaphp.com/guide/about.filesystem">каскадную файловую систему</a></dfn>. Во-вторых, Code Igniter <a href="http://codeigniter.com/user_guide/general/creating_libraries.html">не позволяет</a> расширять или заменять класс <code>Database</code>. А изменять код системы (и поддерживать его при каждом обновлении) очень не хочется. Тем не менее, я нашел простое решение и реализовал соответствующую библиотеку.</p>
<p>Перед использованием библиотеки рекомендуется прочитать статью «<a href="http://dev.mysql.com/doc/refman/5.0/en/using-explain.html">Optimizing Queries with EXPLAIN</a>».</p>
<p><strong>Использование библиотеки.</strong> Библиотека состоит из двух частей:</p>
<ol>
<li><strong>Драйвер базы данных</strong> (каталог <code>system/database/drivers/<a href="http://blog.sjinks.pro/tag/mysql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  MySQL">mysql</a>_exp</code>). Драйвер требует наличия драйвера MySQL (класс <code>CI_DB_mysql_driver</code>), так как, по сути дела, является производным от него классом. Драйвер переопределяет метод <span class="codebox"><code class="php">_execute<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>, в котором и выполняет анализ запроса.</li>
<li><strong>Представление (view).</strong> Находится в каталоге <code>system/application/views/sqlmon</code>, используется для отображения результатов.</li>
</ol>
<p>Установка библиотеки простая: содержимое каталога <code>system/database</code> должен быть скопировано в <code>system/database</code>, содержимое каталога <code>system/application</code> должно быть скопировано в каталог приложения. Затем нужно отредактировать настройки соединения с базой данных (файл <code>appliaction/config/database.<a href="http://blog.sjinks.pro/tag/php/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PHP">php</a></code>): параметр <code>dbdriver</code> соединения нужно установить в <code>mysql_exp</code>.</p>
<p>Пример:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p76937">
        <div class="code php" id="p769code37">
<span class="re0">$active_group</span> <span class="sy0">=</span> <span class="st0">&quot;default&quot;</span><span class="sy0">;</span><br />
<span class="re0">$active_record</span> <span class="sy0">=</span> <span class="kw4">TRUE</span><span class="sy0">;</span><br />
<br />
<span class="re0">$db</span><span class="br0">&#91;</span><span class="st_h">'default'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'hostname'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;localhost&quot;</span><span class="sy0">;</span><br />
<span class="re0">$db</span><span class="br0">&#91;</span><span class="st_h">'default'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'username'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;username&quot;</span><span class="sy0">;</span><br />
<span class="re0">$db</span><span class="br0">&#91;</span><span class="st_h">'default'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'password'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;password&quot;</span><span class="sy0">;</span><br />
<span class="re0">$db</span><span class="br0">&#91;</span><span class="st_h">'default'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'database'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;database&quot;</span><span class="sy0">;</span><br />
<span class="re0">$db</span><span class="br0">&#91;</span><span class="st_h">'default'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'dbdriver'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;mysql_exp&quot;</span><span class="sy0">;</span> <span class="co1">// &lt;&lt;&lt; Вот оно</span><br />
<span class="re0">$db</span><span class="br0">&#91;</span><span class="st_h">'default'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'dbprefix'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;mso_&quot;</span><span class="sy0">;</span><br />
<span class="re0">$db</span><span class="br0">&#91;</span><span class="st_h">'default'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'pconnect'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="kw4">TRUE</span><span class="sy0">;</span><br />
<span class="re0">$db</span><span class="br0">&#91;</span><span class="st_h">'default'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'db_debug'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="kw4">TRUE</span><span class="sy0">;</span><br />
<span class="re0">$db</span><span class="br0">&#91;</span><span class="st_h">'default'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'cache_on'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="kw4">FALSE</span><span class="sy0">;</span><br />
<span class="re0">$db</span><span class="br0">&#91;</span><span class="st_h">'default'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'cachedir'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;system/cache/db&quot;</span><span class="sy0">;</span><br />
<span class="re0">$db</span><span class="br0">&#91;</span><span class="st_h">'default'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'char_set'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;utf8&quot;</span><span class="sy0">;</span><br />
<span class="re0">$db</span><span class="br0">&#91;</span><span class="st_h">'default'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'dbcollat'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">&quot;utf8_general_ci&quot;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Затем в соответствующее представление добавляем код:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p76938">
        <div class="code php" id="p769code38">
<span class="kw2">&lt;?php</span><br />
&nbsp; &nbsp; <span class="re0">$ci</span> <span class="sy0">=</span> <span class="sy0">&amp;</span>get_instance<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="re0">$ci</span><span class="sy0">-&gt;</span><span class="me1">load</span><span class="sy0">-&gt;</span><span class="me1">view</span><span class="br0">&#40;</span><span class="st_h">'sqlmon/sqlmon'</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="sy1">?&gt;</span>
        </div>
    </div>
</div>

<p>И наслаждаемся. На выходе должно получиться что-то вида (на примере MaxSite CMS; изображение можно кликнуть):</p>
<p><a href="http://static.sjinks.info/wp-content/uploads/2010/02/codeigniter-sqlmon-maxsite.png" rel="lightbox" title="Результат работы SQLMon для Code Igniter на примере MaxSite CMS"><img src="http://static.sjinks.info/wp-content/uploads/2010/02/codeigniter-sqlmon-maxsite-300x292.png" alt="Результат работы SQLMon для Code Igniter на примере MaxSite CMS" title="Результат работы SQLMon для Code Igniter на примере MaxSite CMS" width="300" height="292" class="alignnone size-medium wp-image-770" /></a></p>
<p><strong><a href="http://d.sjinks.pro/sqlmon-ci.zip">Скачать SQLMon для Code Igniter</a>.</strong></p>
<p></p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/769-sqlmon-for-code-igniter/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/769-sqlmon-for-code-igniter/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>SQLMon для Kohana 3</title>
		<link>http://blog.sjinks.pro/php/kohana/762-sqlmon-for-kohana-3/</link>
		<comments>http://blog.sjinks.pro/php/kohana/762-sqlmon-for-kohana-3/#comments</comments>
		<pubDate>Tue, 09 Feb 2010 19:16:01 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[Kohana]]></category>
		<category><![CDATA[Kohana 3]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[SQLMon]]></category>
		<category><![CDATA[оптимизация]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=762</guid>
		<description><![CDATA[Мониторинг SQL-запросов в Kohana 3 Продолжая славную традицию реализации SQLMon под различные CMS/фреймворки, написал одному заказчику модуль для Kohana 3. SQLMon для Kohana 3 интегрируется в иерархию классов Database (встраивается между классами Database_MySQL и Kohana_Database_MySQL) и реализует обёртку над методом Kohana_Database_MySQL::query(), измеряя время выполнения запроса, объём потребляемой памяти, записывая код ошибки запроса, трассу вызовов и [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/kohana/762-sqlmon-for-kohana-3/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Мониторинг SQL-запросов в <a href="http://blog.sjinks.pro/tag/kohana/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana">Kohana</a> 3</em></h2>
<p>Продолжая славную традицию реализации <a href="http://blog.sjinks.pro/tag/sqlmon/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  SQLMon">SQLMon</a> под различные CMS/фреймворки, написал одному заказчику модуль для <a href="http://blog.sjinks.pro/tag/kohana-3/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Kohana 3">Kohana 3</a>.</p>
<p>SQLMon для Kohana 3 интегрируется в иерархию классов Database (встраивается между классами <code>Database_<a href="http://blog.sjinks.pro/tag/mysql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  MySQL">MySQL</a></code> и <code>Kohana_Database_MySQL</code>) и реализует обёртку над методом <span class="codebox"><code class="php">Kohana_Database_MySQL<span class="sy0">::</span><span class="me2">query</span><span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>, измеряя время выполнения запроса, объём потребляемой памяти, записывая код ошибки запроса, трассу вызовов и <span class="codebox"><code class="mysql"><span class="kw1">EXPLAIN</span></code></span> запроса (причём не только для <span class="codebox"><code class="mysql"><span class="kw1">SELECT</span></code></span>, но и <span class="codebox"><code class="mysql"><span class="kw1">UPDATE</span></code></span>/<span class="codebox"><code class="mysql"><span class="kw1">DELETE</span></code></span> и <span class="codebox"><code class="mysql"><span class="kw2">INSERT</span></code></span>/<span class="codebox"><code class="mysql"><span class="kw2">REPLACE</span> <span class="kw1">INTO</span> … <span class="kw1">AS</span></code></span> или <span class="codebox"><code class="mysql"><span class="kw1">CREATE</span> &nbsp;<span class="kw1">TABLE</span> … <span class="kw1">AS</span></code></span>) — всё то же самое, что и <a href="http://blog.sjinks.pro/wordpress-plugins/sqlmon/">SQLMon для WordPress</a>.<span id="more-762"></span></p>
<p>Данный модуль будет полезен разработчикам для анализа производительности запросов MySQL и поиска решений для их оптимизации. Перед использованием модуля рекомендуется прочитать статью «<a href="http://dev.mysql.com/doc/refman/5.0/en/using-explain.html">Optimizing Queries with EXPLAIN</a>».</p>
<p><strong>Использование модуля.</strong> Модуль должен быть распакован в каталог <code>modules</code> проекта (после распаковки появится каталог <code>sqlmon</code>). Затем модуль должен быть активирован в файле <code>application/bootstrap.<a href="http://blog.sjinks.pro/tag/php/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PHP">php</a></code>. Очень важно, чтобы модуль подключался <strong>перед модулем <code>database</code></strong> — это связано с <a href="http://v3.kohanaphp.com/guide/about.filesystem">особенностями поиска классов в Kohana 3</a>.</p>
<p>Например:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p76242">
        <div class="code php" id="p762code42">
Kohana<span class="sy0">::</span><span class="me2">modules</span><span class="br0">&#40;</span><span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; <span class="st_h">'sqlmon'</span> &nbsp; &nbsp; <span class="sy0">=&gt;</span> MODPATH<span class="sy0">.</span><span class="st_h">'sqlmon'</span><span class="sy0">,</span> &nbsp; &nbsp; <span class="co1">// SQL Monitor — обязательно перед Database</span><br />
&nbsp; &nbsp; <span class="st_h">'database'</span> &nbsp; <span class="sy0">=&gt;</span> MODPATH<span class="sy0">.</span><span class="st_h">'database'</span><span class="sy0">,</span> &nbsp; <span class="co1">// Database access</span><br />
&nbsp; &nbsp; <span class="st_h">'orm'</span> &nbsp; &nbsp; &nbsp; &nbsp;<span class="sy0">=&gt;</span> MODPATH<span class="sy0">.</span><span class="st_h">'orm'</span><span class="sy0">,</span> &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">// Object Relationship Mapping</span><br />
&nbsp; &nbsp; <span class="st_h">'pagination'</span> <span class="sy0">=&gt;</span> MODPATH<span class="sy0">.</span><span class="st_h">'pagination'</span><span class="sy0">,</span> <span class="co1">// Paging of results</span><br />
<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Для отображения статистики нужно выполнить</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p76243">
        <div class="code php" id="p762code43">
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> View<span class="sy0">::</span><span class="me2">factory</span><span class="br0">&#40;</span><span class="st_h">'sqlmon/sqlmon'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="sy1">?&gt;</span>
        </div>
    </div>
</div>

<p>Результатом будет нечто вида (скриншот можно кликнуть):</p>
<p><a href="http://static.sjinks.info/wp-content/uploads/2010/02/kohana-sqlmon.png" rel="lightbox" title="SQLMon для Kohana — пример работы"><img src="http://static.sjinks.info/wp-content/uploads/2010/02/kohana-sqlmon-300x136.png" alt="SQLMon для Kohana — пример работы" title="SQLMon для Kohana — пример работы" width="300" height="136" class="size-medium wp-image-763" /></a></p>
<p><strong>Настройка.</strong> SQLMon использует файлы конфигурации модуля Database — <code>config/database.php</code>. Для контроля поведения SQLMon нужно использовать параметры:</p>
<ul>
<li><code>backtrace</code> — <code>boolean</code> (по умолчанию true) — должен ли SQLMon отображать трассу вызовов (то, что отображается синим цветом на скриншоте выше);</li>
<li><code>explain</code> — <code>boolean</code> (по умолчанию true) — должен ли SQLMon отображать план выполнения запросов (<span class="codebox"><code class="mysql"><span class="kw1">EXPLAIN</span></code></span>).</li>
</ul>
<p>Пример файла конфигурации:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p76244">
        <div class="code php" id="p762code44">
<span class="kw2">&lt;?php</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'default'</span> <span class="sy0">=&gt;</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'type'</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="st_h">'mysql'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'profiling'</span> &nbsp; &nbsp; <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'charset'</span> &nbsp; &nbsp; &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'utf8'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'table_prefix'</span> &nbsp;<span class="sy0">=&gt;</span> <span class="st_h">''</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Настройки SQLMon</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'backtrace'</span> &nbsp; &nbsp; <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'explain'</span> &nbsp; &nbsp; &nbsp; <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// ---</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'connection'</span> &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw1">array</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'username'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'root'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'password'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'M1dn19ht'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'hostname'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'localhost'</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'persistent'</span> <span class="sy0">=&gt;</span> <span class="kw4">false</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st_h">'database'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st_h">'feedfetcher'</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="sy1">?&gt;</span>
        </div>
    </div>
</div>

<p><strong><a href="http://d.sjinks.pro/kohana/sqlmon.zip">Скачать SQLMon для Kohana 3</a>.</strong></p>
<p></p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/kohana/762-sqlmon-for-kohana-3/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/kohana/762-sqlmon-for-kohana-3/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Ужасы таксономии в WordPress</title>
		<link>http://blog.sjinks.pro/wordpress/575-horror-of-taxonomy-in-wordpress/</link>
		<comments>http://blog.sjinks.pro/wordpress/575-horror-of-taxonomy-in-wordpress/#comments</comments>
		<pubDate>Tue, 09 Jun 2009 01:01:42 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[оптимизация]]></category>
		<category><![CDATA[производительность]]></category>
		<category><![CDATA[таксономия]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=575</guid>
		<description><![CDATA[12,000 запросов для удаления одной категории Внутренняя реализация управления таксономиями в WordPress — это просто кошмар какой-то. Мало того, что код написан в процедурном стиле (использование ООП помогло бы решить некоторые проблемы с производительностью, которые иначе можно решить только глобальными переменными), он к тому же очень плохо масштабируется. Я взял свежую дефолтную инсталляцию WordPress 2.8-bleeding, сгенерировал тестовый контент [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/wordpress/575-horror-of-taxonomy-in-wordpress/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>12,000 запросов для удаления одной категории</em></h2>
<p>Внутренняя реализация управления <a href="http://blog.sjinks.pro/wordpress/338-taxonomy-in-wordpress/">таксономиями в WordPress</a> — это просто кошмар какой-то. Мало того, что код написан в процедурном стиле (использование ООП помогло бы решить некоторые проблемы с производительностью, которые иначе можно решить только глобальными переменными), он к тому же очень плохо масштабируется.</p>
<p>Я взял свежую дефолтную инсталляцию <a href="http://blog.sjinks.pro/tag/wordpress/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  WordPress">WordPress</a> 2.8-bleeding, сгенерировал тестовый контент и пошел искать недоработки и проблемы с производительностью. Одну из них я нашел очень быстро: при попытке удалить категорию, в которой была 1,001 <del datetime="2009-06-08T23:11:21+00:00">ночь</del> запись.</p>
<p>На неслабом сервере это заняло около 20 секунд и… более 12,000 запросов (да-да, двенадцати тысяч, я количеством ноликов, увы, не ошибся).<span id="more-575"></span></p>
<p><strong><a href='http://static.sjinks.info/wp-content/uploads/2009/06/delete-category.html.gz' rel="nofollow">Лог запросов (application/x-gzip, 75.1 КиБ)</a></strong><br />
<strong><a href='http://static.sjinks.info/wp-content/uploads/2009/06/delete-category.html' rel="nofollow">Лог запросов (text/html, 2.5 МиБ)</a></strong></p>
<p>А всё почему? Из-за одного магического <span class="codebox"><code class="php"><span class="kw1">foreach</span></code></span> в <span class="codebox"><code class="php">wp_delete_term<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p57549">
        <div class="code php" id="p575code49">
&nbsp; &nbsp; <span class="kw1">foreach</span> <span class="br0">&#40;</span> <span class="br0">&#40;</span><span class="kw1">array</span><span class="br0">&#41;</span> <span class="re0">$objects</span> <span class="kw1">as</span> <span class="re0">$object</span> <span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$terms</span> <span class="sy0">=</span> wp_get_object_terms<span class="br0">&#40;</span><span class="re0">$object</span><span class="sy0">,</span> <span class="re0">$taxonomy</span><span class="sy0">,</span> <span class="st_h">'fields=ids'</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span> 1 <span class="sy0">==</span> <span class="kw3">count</span><span class="br0">&#40;</span><span class="re0">$terms</span><span class="br0">&#41;</span> <span class="sy0">&amp;&amp;</span> <span class="kw1">isset</span><span class="br0">&#40;</span><span class="re0">$default</span><span class="br0">&#41;</span> <span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$terms</span> <span class="sy0">=</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="re0">$default</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$terms</span> <span class="sy0">=</span> <span class="kw3">array_diff</span><span class="br0">&#40;</span><span class="re0">$terms</span><span class="sy0">,</span> <span class="kw1">array</span><span class="br0">&#40;</span><span class="re0">$term</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$terms</span> <span class="sy0">=</span> <span class="kw3">array_map</span><span class="br0">&#40;</span><span class="st_h">'intval'</span><span class="sy0">,</span> <span class="re0">$terms</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; wp_set_object_terms<span class="br0">&#40;</span><span class="re0">$object</span><span class="sy0">,</span> <span class="re0">$terms</span><span class="sy0">,</span> <span class="re0">$taxonomy</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Куда уж проще: выбрать ID всех объектов, связанных с данной таксономией:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p57550">
        <div class="code mysql" id="p575code50">
<span class="kw1">SELECT</span> <span class="kw1">DISTINCT</span> tr.object_id<br />
<span class="kw1">FROM</span> wp_term_taxonomy <span class="kw1">AS</span> tt <span class="kw1">INNER</span> <span class="kw1">JOIN</span> wp_term_relationships <span class="kw1">AS</span> tr <span class="kw1">USING</span><span class="br0">&#40;</span>term_taxonomy_id<span class="br0">&#41;</span><br />
<span class="kw1">WHERE</span> tr.term_id <span class="sy1">=</span> <span class="st0">'TERM ID HERE'</span> <span class="kw10">AND</span> tr.taxonomy <span class="sy1">=</span> <span class="st0">'TAXONOMY NAME HERE'</span>
        </div>
    </div>
</div>

<p>Затем удалить терм к едрене фене и жукам майским:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p57551">
        <div class="code mysql" id="p575code51">
<span class="kw1">DELETE</span><br />
<span class="kw1">FROM</span> wp_term_taxonomy <span class="kw1">AS</span> tt <span class="kw13">LEFT</span> <span class="kw1">JOIN</span> wp_term_relationships <span class="kw1">AS</span> tr <span class="kw1">USING</span><span class="br0">&#40;</span>term_taxonomy_id<span class="br0">&#41;</span><br />
<span class="kw1">WHERE</span> tr.term_id <span class="sy1">=</span> <span class="st0">'TERM ID HERE'</span> <span class="kw10">AND</span> tr.taxonomy <span class="sy1">=</span> <span class="st0">'TAXONOMY NAME HERE'</span>
        </div>
    </div>
</div>

<p>Затем вставить дефолтное значение терма для тех объектов, которые выпали из таксономии: вычитаем из результата, полученного в первом запросе, результат запроса <span class="codebox"><code class="mysql"><span class="kw1">SELECT</span> object_id <span class="kw1">FROM</span> wp_term_relationship <span class="kw1">WHERE</span> object_id <span class="kw2">IN</span> <span class="br0">&#40;</span><span class="st0">&quot;список из первого запроса&quot;</span><span class="br0">&#41;</span></code></span>. Для полученного результата выполнить один большой <span class="codebox"><code class="mysql"><span class="kw2">INSERT</span></code></span>:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p57552">
        <div class="code mysql" id="p575code52">
<span class="kw2">INSERT</span> <span class="kw1">INTO</span> wp_term_relationships <span class="kw1">VALUES</span><br />
<span class="br0">&#40;</span><span class="st0">'OBJECT<span class="es1">_</span>ID<span class="es1">_</span>1'</span><span class="sy2">,</span> <span class="st0">'DEFAULT<span class="es1">_</span>TERM<span class="es1">_</span>TAXONOMY<span class="es1">_</span>ID'</span><span class="sy2">,</span> <span class="nu0">0</span><span class="br0">&#41;</span><span class="sy2">,</span> <span class="coMULTI">/* ... */</span> <span class="br0">&#40;</span><span class="st0">'OBJECT<span class="es1">_</span>ID<span class="es1">_</span>N'</span><span class="sy2">,</span> <span class="st0">'DEFAULT<span class="es1">_</span>TERM<span class="es1">_</span>TAXONOMY<span class="es1">_</span>ID'</span><span class="sy2">,</span> <span class="nu0">0</span><span class="br0">&#41;</span>
        </div>
    </div>
</div>

<p>После чего сделать <span class="codebox"><code class="mysql"><span class="kw1">UPDATE</span> wp_term_taxonomy <span class="kw1">SET</span> <span class="st0">`count`</span> <span class="sy1">=</span> <span class="st0">`count`</span> <span class="sy1">+</span> <span class="st0">'количество строк, затронутых INSERT'</span> <span class="kw1">WHERE</span> term_taxonomy_id <span class="sy1">=</span> <span class="st0">'DEFAULT<span class="es1">_</span>TERM<span class="es1">_</span>TAXONOMY<span class="es1">_</span>ID'</span></code></span>.</p>
<p>Причём если дефолтное значение терма не задано, то достаточно просто одного <span class="codebox"><code class="mysql"><span class="kw1">DELETE</span></code></span>.</p>
<p>В результате получаем пять запросов вместо 12,000, улучшенную <a href="http://blog.sjinks.pro/tag/performance/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  производительность">производительность</a> и меньше мусора. За кадром осталась очистка <a href="http://blog.sjinks.pro/wordpress/410-monstrosa-horribilis/"><del datetime="2009-06-08T23:11:21+00:00">инсулинового</del> кэша WordPress</a>, но идея понятна. Вот только жаль, что оно увидит свет очень нескоро.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/wordpress/575-horror-of-taxonomy-in-wordpress/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/wordpress/575-horror-of-taxonomy-in-wordpress/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Увеличение производительности плагина NextGen Gallery</title>
		<link>http://blog.sjinks.pro/wordpress/572-improving-nextgen-gallery-performance/</link>
		<comments>http://blog.sjinks.pro/wordpress/572-improving-nextgen-gallery-performance/#comments</comments>
		<pubDate>Sat, 06 Jun 2009 10:27:11 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[NextGen Gallery]]></category>
		<category><![CDATA[плагин]]></category>
		<category><![CDATA[производительность]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=572</guid>
		<description><![CDATA[Добавляем необходимые индексы в таблицу В случае, если галереи содержат несколько тысяч изображений, в зависимости от мощности сервера и посещаемости сайта могут возникнуть проблемы с производительностью, связанные с неоптимальностью индексов в таблице wp_ngg_pictures. Проявляется это в том, что при отображении любой галереи MySQL приходится полностью сканировать таблицу wp_ngg_pictures. Например: EXPLAIN SELECT SQL_CALC_FOUND_ROWS tt.*, t.* FROM [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/wordpress/572-improving-nextgen-gallery-performance/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Добавляем необходимые индексы в таблицу</em></h2>
<p>В случае, если галереи содержат несколько тысяч изображений, в зависимости от мощности сервера и посещаемости сайта могут возникнуть проблемы с производительностью, связанные с неоптимальностью индексов в таблице <code>wp_ngg_pictures</code>.<span id="more-572"></span></p>
<p>Проявляется это в том, что при отображении любой галереи <a href="http://blog.sjinks.pro/tag/mysql/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  MySQL">MySQL</a> приходится полностью сканировать таблицу <code>wp_ngg_pictures</code>.</p>
<p>Например:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p57257">
        <div class="code mysql" id="p572code57">
<span class="kw1">EXPLAIN</span><br />
<span class="kw1">SELECT</span> <span class="kw1">SQL_CALC_FOUND_ROWS</span> tt.<span class="sy1">*</span><span class="sy2">,</span> t.<span class="sy1">*</span><br />
<span class="kw1">FROM</span> wp_ngg_gallery <span class="kw1">AS</span> t <span class="kw1">INNER</span> <span class="kw1">JOIN</span> wp_ngg_pictures <span class="kw1">AS</span> tt <span class="kw1">ON</span> t.gid <span class="sy1">=</span> tt.galleryid<br />
<span class="kw1">WHERE</span> t.gid <span class="sy1">=</span> 3 <span class="kw10">AND</span> tt.exclude<span class="sy1">&lt;&gt;</span>1<br />
<span class="kw1">ORDER BY</span> tt.sortorder <span class="kw1">ASC</span>
        </div>
    </div>
</div>

          
<div class="codebox">
    <div class="the_code" style="" id="p57258">
        <div class="code text" id="p572code58">
+----+-------------+-------+-------+---------------+---------+---------+-------+--------+----------------+<br />
| id | select_type | table | type &nbsp;| possible_keys | key &nbsp; &nbsp; | key_len | ref &nbsp; | rows &nbsp; | Extra &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br />
+----+-------------+-------+-------+---------------+---------+---------+-------+--------+----------------+<br />
| &nbsp;1 | SIMPLE &nbsp; &nbsp; &nbsp;| t &nbsp; &nbsp; | const | PRIMARY &nbsp; &nbsp; &nbsp; | PRIMARY | 8 &nbsp; &nbsp; &nbsp; | const | &nbsp; &nbsp; &nbsp;1 | Using filesort |<br />
| &nbsp;1 | SIMPLE &nbsp; &nbsp; &nbsp;| tt &nbsp; &nbsp;| ALL &nbsp; | NULL &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| NULL &nbsp; &nbsp;| NULL &nbsp; &nbsp;| NULL &nbsp;| &nbsp;11700 | Using where &nbsp; &nbsp;|<br />
+----+-------------+-------+-------+---------------+---------+---------+-------+--------+----------------+
        </div>
    </div>
</div>

<p>Исправляется ситуация путём добавления ключа по столбцам <code>galleryid</code> и <code>sortorder</code> в таблицу <code>wp_ngg_pictures</code>:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p57259">
        <div class="code mysql" id="p572code59">
<span class="kw1">ALTER</span> <span class="kw1">TABLE</span> <span class="st0">`wp<span class="es1">_</span>ngg<span class="es1">_</span>pictures`</span> <span class="kw1">ADD</span> <span class="kw1">KEY</span> <span class="br0">&#40;</span><span class="st0">`galleryid`</span><span class="sy2">,</span> <span class="st0">`sortorder`</span><span class="br0">&#41;</span>
        </div>
    </div>
</div>

<p>После этого <span class="codebox"><code class="mysql"><span class="kw1">EXPLAIN</span></code></span> покажет значительно лучшую картину:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p57260">
        <div class="code text" id="p572code60">
+----+-------------+-------+-------+---------------+-----------+---------+-------+------+-------------+<br />
| id | select_type | table | type &nbsp;| possible_keys | key &nbsp; &nbsp; &nbsp; | key_len | ref &nbsp; | rows | Extra &nbsp; &nbsp; &nbsp; |<br />
+----+-------------+-------+-------+---------------+-----------+---------+-------+------+-------------+<br />
| &nbsp;1 | SIMPLE &nbsp; &nbsp; &nbsp;| t &nbsp; &nbsp; | const | PRIMARY &nbsp; &nbsp; &nbsp; | PRIMARY &nbsp; | 8 &nbsp; &nbsp; &nbsp; | const | &nbsp; &nbsp;1 | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br />
| &nbsp;1 | SIMPLE &nbsp; &nbsp; &nbsp;| tt &nbsp; &nbsp;| ref &nbsp; | galleryid &nbsp; &nbsp; | galleryid | 8 &nbsp; &nbsp; &nbsp; | const | &nbsp; 59 | Using where |<br />
+----+-------------+-------+-------+---------------+-----------+---------+-------+------+-------------+
        </div>
    </div>
</div>

<p>Как видим, использование правильного индекса позволяет избежать файловой сортировки; к тому же, из таблицы <code>wp_ngg_pictures</code> сразу выбираются только нужные данные (59 строк против 11,700 строк).</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/wordpress/572-improving-nextgen-gallery-performance/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/wordpress/572-improving-nextgen-gallery-performance/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

