<?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>Записки искателей &#187; MySQL</title>
	<atom:link href="http://voituk.kiev.ua/category/mysql/feed/" rel="self" type="application/rss+xml" />
	<link>http://voituk.kiev.ua</link>
	<description>while ( isAlive() ) {doCode(); doFun();}</description>
	<lastBuildDate>Sun, 29 Apr 2012 18:50:14 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>MySQL: Slave could not parse relay log event entry</title>
		<link>http://voituk.kiev.ua/2009/09/17/mysql-slave-could-not-parse-relay-log-event-entry/</link>
		<comments>http://voituk.kiev.ua/2009/09/17/mysql-slave-could-not-parse-relay-log-event-entry/#comments</comments>
		<pubDate>Thu, 17 Sep 2009 08:09:31 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=1079</guid>
		<description><![CDATA[При использованиии MySQL Master-Slave (или же Master-Master) репликации бывает ситуация что по разным причинам версия слейва ниже чем версия мастера. Если при этом используется опция binlog_format=MIXED, в результате возможна ситуация когда мастер запишет в свой binlog запись вида: BINLOG 'XMawShMEAAAARwAAAEp5UwUAAGkAAAAAAAAACHNhblahblahblah' Которую слейв не сможет выполнить, ибо он ее попросту &#8220;еще не умеет&#8221;. Первой ласточкой того, [...]]]></description>
			<content:encoded><![CDATA[<p>При использованиии MySQL Master-Slave (или же Master-Master) репликации бывает ситуация что по разным причинам версия слейва ниже чем версия мастера.<br />
Если при этом используется опция <em>binlog_format=MIXED</em>, в результате возможна ситуация когда мастер запишет в свой binlog запись вида:<br />
<code>BINLOG 'XMawShMEAAAARwAAAEp5UwUAAGkAAAAAAAAACHNhblahblahblah'</code><br />
Которую слейв не сможет выполнить, ибо он ее попросту &#8220;еще не умеет&#8221;.</p>
<p>Первой ласточкой того, что это произошло станет сообщение &#8220;SLAVE DOWN&#8221; от мониторинговой системы и запись в MySQL-логе слейва сродни этой:</p>
<p><code>090917 14:55:51 [ERROR] Error in Log_event::read_log_event(): 'Found invalid event in binary log', data_len: 71, event_type: 19<br />
090917 14:55:51 [ERROR] Error reading relay log event: slave SQL thread aborted because of I/O error<br />
090917 14:55:51 [ERROR] Slave: Could not parse relay log event entry. The possible reasons are: the master's binary log is corrupted (you can check this by running 'mysqlbinlog' o<br />
n the binary log), the slave's relay log is corrupted (you can check this by running 'mysqlbinlog' on the relay log), a network problem, or a bug in the master's or slave's MySQL<br />
code. If you want to check the master's binary log or slave's relay log, you will be able to know their names by issuing 'SHOW SLAVE STATUS' on this slave. Error_code: 0<br />
090917 14:55:51 [ERROR] Error running query, slave SQL thread aborted. Fix the problem, and restart the slave SQL thread with "SLAVE START". We stopped at log 'bin-us1.000022' position 17007149<br />
</code></p>
<p>Попытки пропустить неугодные записи старым добрым методом:<br />
<code>slave stop;<br />
set global sql_slave_skip_counter=1;<br />
slave start;</code><br />
в данном случае не помогают.</p>
<p>Как вариант решения предлагаю:</p>
<ol>
<li>Останавливаем slave: <em>&#8220;SLAVE STOP;&#8221;</em></li>
<li>&#8220;Выуживаем&#8221; из текста ошибки имя бинлога и позицию, на которой произошла ошибка.<br />
В данном случае это <em>bin-us1.000022</em> и позиция <em>17007149</em></li>
<li>На мастере выполняем запрос <em>&#8220;FLUSH LOGS;&#8221;</em></li>
<li>Делаем дамп бинлога начиная с найденной позиции:<br />
<code>mysqlbinlog --start-position=17007149 bin-us1.000022  &gt;  bin-us1-from17007149.sql</code></li>
<li>Загружаем полученный дамп на слейве с ключем &#8220;&#8211;force&#8221;<br />
<code>mysql --force -uuser -ppassword database &lt;  bin-us1-from17007149.sql</code></li>
<li>Выполняем на слейве &#8220;FLUSH LOGS;&#8221;</li>
<li>В data-директории находим relay-log с максимальным номером и размером около 100 байт (в моем случае это mysqld-relay-bin.000037)</li>
<li>В той же директории в файле relay-log.info<br />
первую строку заменяем на имя только что найденного relay-log-а, во вторую вписываем 0, в третью &#8211; следующий после &#8220;испорченного&#8221; бинлог, в четвертую &#8211; 0<br />
Получится что-то вроде:<br />
<code>/var/lib/mysql/mysqld-relay-bin.000037<br />
0<br />
bin-us1.000023<br />
0</code></li>
<li>Дальше на слейве выполняем:<br />
<code>CHANGE MASTER TO MASTER_LOG_FILE='bin-us1.000023', MASTER_LOG_POS=0;<br />
SLAVE START;</code></li>
<li>Ну и потом чтоб удостоверится что все &#8220;завелось&#8221;<br />
<code>SHOW SLAVE STATUS\G</code></li>
</ol>
<p>Должно работать.<br />
Подозреваю что пункты 4 и 5 можно обьеденить в один, более простой и элегантный.<br />
Но как это сделать &#8211; не гуглил.</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2009/09/17/mysql-slave-could-not-parse-relay-log-event-entry/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>MySQL: Single row lock implementation</title>
		<link>http://voituk.kiev.ua/2009/06/24/mysql-single-row-lock-implementation/</link>
		<comments>http://voituk.kiev.ua/2009/06/24/mysql-single-row-lock-implementation/#comments</comments>
		<pubDate>Wed, 24 Jun 2009 14:01:35 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=1066</guid>
		<description><![CDATA[При разработке приложений с использованием баз данных, часто возникает сценарий &#8220;insert if not exists&#8221;: if ( &#60;row exists in table&#62; ) then &#60;insert new record into table&#62; Из-за того, что данный блок псевдо-кода не выполяняется атомано, может возникнуть ситуация, когда между проверкой &#60;row exists in table&#62; и выполнением действия &#60;insert new record&#62; паралельный поток (клиент)  [...]]]></description>
			<content:encoded><![CDATA[<p>При разработке приложений с использованием баз данных, часто возникает сценарий &#8220;insert if not exists&#8221;: </p>
<pre><code class="sql">if ( &lt;row exists in table&gt; )
   then &lt;insert new record into table&gt;</code></pre>
<p>Из-за того, что данный блок псевдо-кода не выполяняется атомано, может возникнуть ситуация, когда между проверкой <em>&lt;row exists in table&gt;</em> и выполнением действия <em>&lt;insert new record&gt;</em> паралельный поток (клиент)  выполнит добавление новой записи и в итоге в БД окажется 2 одинаковых записи (или произойдет ошибка &#8220;duplicate key&#8221;).</p>
<p>Такой, не самый приятный case, в книгах по паралельному программированию называют race condition и обходят путем создания синхронизирующих блокировок , использованием &#8220;выпрямителей&#8221; и тд.<br />
<span id="more-1066"></span><br />
С точки зрения MySQL надо бы переписать приведенный псевдокод в таком виде:</p>
<pre><code class="sql">mysql_query('LOCK TABLE `table` WRITE')

if ( &lt;row exists in table&gt; )
   then &lt;insert new record into table&gt;

mysql_query('UNLOCK TABLES') </code></pre>
<p>В таком случае таблица будет заблокирована для записи и мы будем уверены что во время выполнения нашего кода, никто, кроме текущего потока, ничего в таблицу не &#8220;дозапишет&#8221; и race condition не возникнет.</p>
<p>Способ вроде простой, и если мне не изменяет память, даже рекомендовался в какой-то книге по разработке web-приложений.<br />
Самый же существенный его минус &#8211; он блокирует <strong>ВСЮ</strong> таблицу и все запросы на запись в эту таблицу будут ждать окончания выполнения блока кода и вызова &#8220;UNLOCK TABLES&#8221;.<br />
Это, в условиях интенсивной записи в таблицу, может создать весьма критичный bottleneck в производительности всей системы.</p>
<p>Очень неплохо бы в таком случае выполнять блокировку на уровне не всей таблицы, а только определенной строки. Немного покопавшись в мануале MySQL, отыскал несколько встроенных функций, которые помогают это реализовать:</p>
<dl>
<dt><a href="http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_get-lock" rel="nofollow">GET_LOCK(str, timeout)</a></dt>
<dd>Создает глобальный мютекс с именем <em>str</em> и временем жизни <em>timeout</em>. При попытке другого потока &#8220;занять&#8221; уже созданный мютекс, он будет ожидать пока он не освободится вызовом RELEASE_LOCK() или не пройдет <em>timeout</em> секунд</dd>
<dt><a href="http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_release-lock" rel="nofollow">RELEASE_LOCK(str)</a></dt>
<dd>Освобождает глобальный мютекс с именем <em>str</em></dd>
</dl>
<p>Кроме того есть еще 2 вспомогательные фунцкции для проверки создан ли уже мютекс, и если создан, то каким ProcessID: <a href="http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_is-free-lock" rel="nofollow">IS_FREE_LOCK()</a> и <a href="http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_is-used-lock" rel="nofollow">IS_USED_LOCK()</a></p>
<p>С учетом вышесказанного описанный выше псевдокод, с использованием row-based блокировки будет выглядеть таким образом:</p>
<pre><code class="sql">mysql_query('DO GET_LOCK("my-prefix.my-row-id", 60)')

if ( &lt;row exists in table&gt; )
   then &lt;insert new record into table&gt;

mysql_query('DO RELEASE_LOCK("my-prefix.my-row-id")') </code></pre>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2009/06/24/mysql-single-row-lock-implementation/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>MySQL: Archive Storage Engine</title>
		<link>http://voituk.kiev.ua/2009/03/13/mysql-archive-storage-engine/</link>
		<comments>http://voituk.kiev.ua/2009/03/13/mysql-archive-storage-engine/#comments</comments>
		<pubDate>Fri, 13 Mar 2009 18:56:24 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=974</guid>
		<description><![CDATA[Все очевидно, что любая современная информацонная система занимается хранением и анализом любого рода данных.  Аналитики утверждают что в среднем, обьем этих данных для среднестатистичекой компании растет на 42% в год. В результате проблема хранения этих данных постепенно приобретает бОльшую актуальность. Из своего опыта работы с достаточно большими массивами данных, хранимых в современных реляционных СУБД (по [...]]]></description>
			<content:encoded><![CDATA[<p>Все очевидно, что любая современная информацонная система занимается хранением и анализом любого рода данных.  Аналитики утверждают что в среднем, обьем этих данных для среднестатистичекой компании растет на 42% в год.<br />
В результате проблема хранения этих данных постепенно приобретает бОльшую актуальность.</p>
<p>Из своего опыта работы с достаточно большими массивами данных, хранимых в современных реляционных СУБД (по большей части в MySQL) могу сказать что не менее 50% данных в любой системе &#8211; это данные которые представляют собой только &#8220;историческую&#8221; ценность.<span id="more-974"></span></p>
<p>Чаще всего по ним просчитаны-пересчитына все возможные &#8220;срезы&#8221;, построены все отчеты и вероятность их использования в дальнейшем с каждым днем все больше приближается к нулю.<br />
Также часто бывает, что просто удалить подобные данные по разным причинам нельзя.</p>
<p>Будучи чуть более молодым и менее опытным, чем сейчас я делал так: разделял данные на 2 части (старые и новые), со старых делал дапм, архивировал и писал на CD, после чего смело удалял их из MySQL.<br />
Думаю не стоит обьяснять во что выливалась внезапная необходимость поднять старый массив данных для какого-то супер-нового отчета. :)</p>
<p>Вот тут очень кстати вспомнить за что я люблю MySQL &#8211; это его plugable storage engines, которые существуют под любую специфичную задачу.</p>
<p>Потому предлагаю кратко пробежаться по особенностям ARCHIVE storage engine, созданного для хранения &#8220;архивных&#8221; данных максимально эффективно:</p>
<ul>
<li>данные в archive-таблицах хранятся в сжатом библиотекой zlib виде</li>
<li>не поддерживает обновление и удаление даннных (UPDATE/REPLACE/DELETE) &#8211; только добавление (INSERT)</li>
<li>не поддерживает индексы</li>
<li>использует row-based локи</li>
<li>поддерживает strict sql_mode</li>
</ul>
<p>С появлением ARCHIVE  Storage Engine описанный выше use-case существенно преобразился: теперь старые данные не дампятся, а переносятся в &#8220;архивную&#8221; таблицу &#8211; полную копию существующей.<br />
В результате их хранение за долгий период не является очень накладным, а удобство использования и мгновенный доступ посредством SQL сохраняется.</p>
<p>В качестве эксперимента приведу результаты сегодняшнего &#8220;архивирования&#8221; описанным образом одной небольшой таблицы:<br />
<em>(по хорошему надо было взять таблицу побольше, но под рукой такой не оказалось)</em><br />
После разделения данных на &#8220;архивные&#8221; и &#8220;актуальные&#8221; и оптимизации таблицы &#8220;актулаьных&#8221; данных получил такое:</p>
<pre><code>+--------------------+--------+---------------+------------+
| table_name         | engine | total_size_mb | table_rows |
+--------------------+--------+---------------+------------+
| arch_downloads_log | MyISAM |         63.68 |      45801 |
| wap_downloads_log  | MyISAM |         18.67 |      13429 |
+--------------------+--------+---------------+------------+</code></pre>
<p>Итого 69тыс строк данных занимают почти 82Mb дискового пространства.<br />
После удаления индексов из arch_downloads_log и конвертирования в тип ARCHIVE получил:</p>
<pre><code>+--------------------+---------+---------------+------------+
| table_name         | engine  | total_size_mb | table_rows |
+--------------------+---------+---------------+------------+
| arch_downloads_log | ARCHIVE |          1.49 |      45801 |
| wap_downloads_log  | MyISAM  |         18.67 |      13429 |
+--------------------+---------+---------------+------------+</code></pre>
<p>Итого, таблица размером в 64Mb была сжата в 1.5Mb &#8211; тоесть в 42 раза.<br />
<em>(хм&#8230; как интересно совпало с 42%, о которых писалось вначале заметки)</em></p>
<p>Выводы? &#8211; Тут уж думайте сами, решайте сами надо ли и поможет ли оно вам.</p>
<p>Ссылки:</p>
<ul>
<li><a href="http://dev.mysql.com/doc/refman/5.0/en/archive-storage-engine.html">Документация об ARCHIVE storage engine от MySQL AB</a></li>
<li><a href="http://dev.mysql.com/tech-resources/articles/storage-engine.html">Обзор возможностей ARCHIVE storage engine</a> от Robin Schumacher</li>
</ul>
<p>P.S. Перечитал написанное  &#8211; какой-то банальщиной попахивает. Ну и ладно.</p>
<p>P.S.S. Ах, да! Те кто сейчас хочет подискутировать на тему &#8220;MySQL не предназначен для хранения больших обьемов данных&#8221; &#8211; предлагаю даже не начинать, ибо ответ будет единственно-правильный: &#8220;Просто вы не умеете его готовить&#8221;.</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2009/03/13/mysql-archive-storage-engine/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>MySQL: Определение размера таблицы</title>
		<link>http://voituk.kiev.ua/2009/01/08/mysql-table-size/</link>
		<comments>http://voituk.kiev.ua/2009/01/08/mysql-table-size/#comments</comments>
		<pubDate>Thu, 08 Jan 2009 16:11:19 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=920</guid>
		<description><![CDATA[Вот такой вот интересный запрос случайно выудил из документации MySQL: SELECT table_name AS table_name, engine, ROUND(data_length/1024/1024,2) AS total_size_mb, table_rows FROM information_schema.tables WHERE table_schema=DATABASE(); Показывает обьем и количество строк в таблицах MySQL. Результат выглядит приблизительно так: +-------------------+--------+---------------+------------+ &#124; table_name &#124; engine &#124; total_size_mb &#124; table_rows &#124; +-------------------+--------+---------------+------------+ &#124; categories &#124; MyISAM &#124; 0.00 &#124; 17 &#124; [...]]]></description>
			<content:encoded><![CDATA[<p>Вот такой вот интересный запрос случайно выудил из документации MySQL:</p>
<pre><code class="sql">SELECT
    table_name AS table_name,
    engine,
    ROUND(data_length/1024/1024,2) AS total_size_mb,
    table_rows
FROM
    information_schema.tables
WHERE
    table_schema=DATABASE();
</code></pre>
<p>Показывает обьем и количество строк в таблицах MySQL.<br />
Результат выглядит приблизительно так:</p>
<pre><code>+-------------------+--------+---------------+------------+
| table_name        | engine | total_size_mb | table_rows |
+-------------------+--------+---------------+------------+
| categories        | MyISAM |          0.00 |         17 |
| downloadlinks     | InnoDB |         13.02 |      19158 |
| errors            | InnoDB |         15.02 |      84104 |
| lastdownloads     | MEMORY |          3.19 |        524 |
| providers         | MyISAM |          0.00 |         17 |
| starstags         | InnoDB |          1.52 |      14323 |
| tagids            | InnoDB |         35.59 |     759694 |
| tags              | InnoDB |       2036.00 |   21971934 |
| vars              | InnoDB |          0.02 |         51 |
| videocategories   | InnoDB |         49.58 |    1583675 |
| videos            | InnoDB |       1864.00 |    1954427 |
| videos_deleted    | MyISAM |         56.33 |      75889 |
| videostats2       | InnoDB |        271.88 |    3417776 |
| videostats2_daily | InnoDB |          0.02 |        266 |
+-------------------+--------+---------------+------------+</code></pre>
<p>Правда есть один нюанс: на InnoDB-таблицах показывает количество незалоченных в данный момент строк.</p>
<p>P.S. А кто-то пробовал использовать ARCHIVE storage engine?<br />
Как он в плане fail-over и repair?<br />
Поделитесь good/bad experience?</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2009/01/08/mysql-table-size/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>MyISAM and InnoDB auto_increment difference</title>
		<link>http://voituk.kiev.ua/2008/08/22/auto-increment-myisam-vs-innodb/</link>
		<comments>http://voituk.kiev.ua/2008/08/22/auto-increment-myisam-vs-innodb/#comments</comments>
		<pubDate>Fri, 22 Aug 2008 07:53:52 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=569</guid>
		<description><![CDATA[Заметил интересную особенность автоинкрементного поля в InnoDB MySQL Engine В отличии от MyISAM, значение автоинкрементного счетчика может повторяться. Пример: (root@localhost) [test]> CREATE TABLE `innodb` ( `id` int(11) NOT NULL auto_increment, PRIMARY KEY (`id`) ) ENGINE=InnoDB; (root@localhost) [test]> INSERT INTO innodb (id) VALUE(null); (root@localhost) [test]> INSERT INTO innodb (id) VALUE(null); (root@localhost) [test]> INSERT INTO innodb (id) [...]]]></description>
			<content:encoded><![CDATA[<p>Заметил интересную особенность автоинкрементного поля в InnoDB MySQL Engine</p>
<p>В отличии от MyISAM, значение автоинкрементного счетчика может повторяться.<span id="more-569"></span><br />
Пример:</p>
<pre><code>(root@localhost) [test]> CREATE TABLE `innodb` (
  `id` int(11) NOT NULL auto_increment,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;

(root@localhost) [test]> INSERT INTO innodb (id) VALUE(null);
(root@localhost) [test]> INSERT INTO innodb (id) VALUE(null);
(root@localhost) [test]> INSERT INTO innodb (id) VALUE(null);

(root@localhost) [test]> SELECT * FROM innodb;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
+----+
3 rows in set (0.00 sec)

(root@localhost) [test]> DELETE FROM innodb WHERE id=3;</code></pre>
<p>После этого делаем рестарт MySQL-сервера и выполняем:</p>
<pre><code>(root@localhost) [test]> INSERT INTO innodb (id) VALUE(null);

(root@localhost) [test]> SELECT * FROM innodb;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
+----+
3 rows in set (0.00 sec)<code></pre>
<p>И что же мы видим? - Опять запись с id=3, успешно удаленная в самый раз перед рестартом.</p>
<p>При использовании MyISAM, новая запись имела бы id=4 (как впрочем и ожидалось).</p>
<p>Немного погуглив, выяснил что "это не баг - это фича":</p>
<p>AUTO_INCREMENT column behavior is different between MyISAM and InnoDB:<br />
  - MyISAM: Maximum value +1 that has been inserted up to now<br />
  - InnoDB: SELECT MAX(auto_increment_column) + 1</p>
<p>Чем чревато данное поведение, думаю рассказывать не стоит.<br />
Выход тут один - при использовании InnoDB в обязательном порядке все связи строить внешними ключами с каскадным удалением.</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2008/08/22/auto-increment-myisam-vs-innodb/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>PHP, MySQL и FOUND_ROWS() bug</title>
		<link>http://voituk.kiev.ua/2008/06/05/php-mysql-found_rows-bug/</link>
		<comments>http://voituk.kiev.ua/2008/06/05/php-mysql-found_rows-bug/#comments</comments>
		<pubDate>Thu, 05 Jun 2008 09:53:50 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=497</guid>
		<description><![CDATA[&#8220;Словил&#8221; сегодня пренеприятнейший баг, который стоил мне более часа потерянного времени. Выполняем в консольном mysql-клиенте такой запрос: SELECT SQL_CALC_FOUND_ROWS FROM tablename LIMIT 5; Получем resultset из 5ти результатов (предполагается, что в таблице tablename больше 5ти записей) Теперь в этом же соединении выполняем: SELECT FOUND_ROWS(); Получаем значение, равное количеству строк в tablename &#8211; ничего сверхъестественного, все [...]]]></description>
			<content:encoded><![CDATA[<p>&#8220;Словил&#8221; сегодня пренеприятнейший баг, который стоил мне более часа потерянного времени.</p>
<p>Выполняем в <a href="http://voituk.kiev.ua/2007/10/19/mysql-client-secrets/">консольном mysql-клиенте</a> такой запрос:<br />
<code>SELECT SQL_CALC_FOUND_ROWS FROM tablename LIMIT 5;</code></p>
<p>Получем resultset из 5ти результатов (предполагается, что в таблице tablename больше 5ти записей)</p>
<p>Теперь в этом же соединении выполняем:<br />
<code>SELECT FOUND_ROWS();</code><br />
Получаем значение, равное количеству строк в tablename &#8211; ничего сверхъестественного, все как и ожидалось.</p>
<p>Самое интересное начинается если повторить выполнить эти SQL-запросы из PHP-скрипта (при определенных условиях, но о оних ниже) &#8211; второй запрос будет всегда возвращать 0.</p>
<p>Причиной всему &#8211; <noindex><a rel="nofollow" href="http://bugs.php.net/bug.php?id=33021">ошибка в модуле php_mysql.so (BUG#33021)</a></noindex>, которая наблюдается при включенной опции<br />
<code>mysql.trace_mode=true</code></p>
<p>Дабы не поулчить подобную неприятность в своих проектах рекомендую пока установить <noindex><a rel="nofollow" href="http://www.php.net/manual/en/mysql.configuration.php#ini.mysql.trace-mode">mysql.trace_mode</a></noindex> в false.</p>
<p>При тестировании использовался PHP 5.2.1 и с модулем mysql client API 5.1.16-beta.</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2008/06/05/php-mysql-found_rows-bug/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Знак зодиака средствами MySQL</title>
		<link>http://voituk.kiev.ua/2008/02/13/zodiak-sign-using-mysql/</link>
		<comments>http://voituk.kiev.ua/2008/02/13/zodiak-sign-using-mysql/#comments</comments>
		<pubDate>Wed, 13 Feb 2008 11:45:50 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/2008/02/13/zodiak-sign-using-mysql/</guid>
		<description><![CDATA[Что-то давно я ничего про MySQL не писал &#8211; буду исправляться. Часто на развлекательных сайтах приходится приходится &#8220;вычислять&#8221; знак зодиака пользователя по его дате рождения. Задача решена уже тысячу раз, и интереса не представляет &#8211; проверка попадания даты в интервал релизуется на любом популярном языке программирования в 5-7 строк. А теперь представьте себе, что нужно [...]]]></description>
			<content:encoded><![CDATA[<p><em>Что-то давно я ничего про MySQL не писал &#8211; буду исправляться.</em></p>
<p>Часто на развлекательных сайтах приходится приходится &#8220;вычислять&#8221; знак зодиака пользователя по его дате рождения. Задача решена уже тысячу раз, и интереса не представляет &#8211; проверка попадания даты в интервал релизуется на любом популярном языке программирования в 5-7 строк.<br />
А теперь представьте себе, что нужно написать SQL-запрос в MySQL БД, который выбрает из таблицы всех пользователей с определенным знаком зодиака.</p>
<p><noindex>Тут на помощь приходит <a href="http://dev.mysql.com/doc/refman/4.1/en/control-flow-functions.html#operator_case" rel="nofollow">оператор CASE</a></noindex>.<br />
С его использованием запрос, который выводит из таблицы <em>users</em> id-пользователя и его знак зодиака выглядит приблизительно так:<br />
[sql]</p>
<pre>SELECT id,
  stat_birth,
  @d:=DAY(FROM_UNIXTIME(stat_birth)) AS stat_birth_day,
  @m:=MONTH(FROM_UNIXTIME(stat_birth)) AS stat_birth_month,
  CASE
    WHEN (@m=3 AND @d&gt;20) OR (@m=4 AND @d&lt;21) THEN 'Oven'
    WHEN (@m=4 AND @d&gt;20) OR (@m=5 AND @d&lt;22) THEN 'Taurus'
    WHEN (@m=5 AND @d&gt;21) OR (@m=6 AND @d&lt;22) THEN 'Gemini'
    WHEN (@m=6 AND @d&gt;21) OR (@m=7 AND @d&lt;23) THEN 'Cancer'
    WHEN (@m=7 AND @d&gt;22) OR (@m=8 AND @d&lt;24) THEN 'Leo'
    WHEN (@m=8 AND @d&gt;23) OR (@m=9 AND @d&lt;24) THEN 'Virgo'
    WHEN (@m=9 AND @d&gt;23) OR (@m=10 AND @d&lt;24) THEN 'Libra'
    WHEN (@m=10 AND @d&gt;23) OR (@m=11 AND @d&lt;23) THEN 'Scorpion'
    WHEN (@m=11 AND @d&gt;22) OR (@m=12 AND @d&lt;22) THEN 'Sagittarius'
    WHEN (@m=12 AND @d&gt;21) OR (@m=1 AND @d&lt;21) THEN 'Capricorn'
    WHEN (@m=1 AND @d&gt;20) OR (@m=2 AND @d&lt;19) THEN 'Aquarius'
    WHEN (@m=2 AND @d&gt;18) OR (@m=3 AND @d&lt;21) THEN 'Fish'
  END AS zodiak
FROM users</pre>
<p>[/sql]</p>
<p>По хорошему определение знака зодиака нужно оформить как хранимую процедуру, но в таком случае теряется совместимость с MySQL версий ниже 5-й.</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2008/02/13/zodiak-sign-using-mysql/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>10 cекретов консольного клиента MySQL</title>
		<link>http://voituk.kiev.ua/2007/10/19/mysql-client-secrets/</link>
		<comments>http://voituk.kiev.ua/2007/10/19/mysql-client-secrets/#comments</comments>
		<pubDate>Fri, 19 Oct 2007 11:24:42 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://dev.voituk.kiev.ua/2007/10/19/mysql-client-secrets/</guid>
		<description><![CDATA[Всем известно, что в пакете с СУБД MySQL поставляется примитивный консольный клиент. Но при этом мало кто им всерьез пользуется, предпочитая GUI- и WEB-аналоги (например MySQL Front, phpMyAdmin). Большинство аргументирует это тем, что консольный mysql неудобен. После 2х лет работы исключительно с консольным клиентом, я с ними категорически не согласен, и уверен что консольный mysql [...]]]></description>
			<content:encoded><![CDATA[<p>Всем известно, что в пакете с СУБД MySQL поставляется примитивный консольный клиент.<br />
Но при этом мало кто им всерьез пользуется, предпочитая GUI- и WEB-аналоги (например MySQL Front, phpMyAdmin). Большинство аргументирует это тем, что консольный mysql неудобен.<br />
После 2х лет работы исключительно с консольным клиентом, я с ними категорически не согласен, и уверен что консольный mysql &#8211; это мощный и удобный инструмент, как vim, только в своей нише.<span id="more-278"></span></p>
<p>Большинство описанных возможностей актуальны для всех версий MySQL, но некоторые будут работать начиная с версии 4.1 и выше.</p>
<p><strong>1.  \? ,  help </strong><br />
Если выполнить без параметров, то отобразится справка по всем командам, но если после команды указать имя функции или оператора MySQL, то с сервера будет загружена справка по указанной функции.<br />
Пример:</p>
<pre><code class="sql">mysql&gt; help select
# или
mysql&gt; \? LOAD DATA
# или
mysql&gt; help contents #для отображения списка разделов справки</code></pre>
<p><strong>2.  \R , prompt</strong><br />
Устанавливает внешний вид приглашения командной строки MySQL. Также можно указывать с помощью переменной окружения MYSQL_PS1 или параметром &#8211;prompt=&#8230; при запуске клиента mysql.<br />
Например:</p>
<pre><code class="sql"> mysql&gt; prompt (\u@\h) [\d]&gt;
PROMPT set to '(\u@\h) [\d]&gt;'
(root@localhost) [test]&gt;</code></pre>
<p><strong>3. \P, pager и \n,  nopager</strong><br />
Устанавливает/отключает утилиту для просмотра результата запроса.<br />
Пример:
<pre><code class="sql">&gt;\Pless
PAGER set to 'less'</code></pre>
<p>Об этой возможности я <a href="/2006/04/14/mysql-pager-tip/">уже писал раньше</a>.<br />
Это  значение можно указать при старте клиента mysql параметром &#8211;pager=&#8230;</p>
<p><strong>4.\T, tee и \t, notee</strong><br />
Включает/выключает сохранение в файл всех введенных команд, а также их результата.</p>
<pre><code class="sql">mysql&gt;tee hello.txt
Logging to file 'hello.txt'
# ... тут можно выполнять запросы ...
mysql&gt; notee
Outfile disabled.</code></pre>
<p>Удобно если нужно выполнить несколько запросов, а потом методом copy-paste подготовить по полученным данным отчет в Excel.</p>
<p><strong>5. \W, warnings и \w nowarning</strong><br />
Включает/выключает вывод на экран предупреждений (warnings) сразу после выполнения запроса, который их вызвал. Включение данной опции по умолчанию позволяет существенно сэкономить время на поиске причин некорректного выполнения запроса (особенно что скасается DATA TRUNCATION). Можно также указать при старте клиента  параметром &#8211;show-warnings.</p>
<p><strong>6. \G, ego</strong><br />
Выполнение указанного запроса, и вывод результата в развернутом, вертикальном виде. Очень удобно, если результат запроса не помещается в ширину экрана</p>
<pre><code class="sql">&gt;show databases\G
*************************** 1. row ***************************
Database: information_schema
*************************** 2. row ***************************
Database: mysql
*************************** 3. row ***************************
Database: test
3 rows in set (0.00 sec)</code></pre>
<p><strong>7.  \., source</strong><br />
Загружает и выполняет SQL-инструкции из файла. Можно использовать для загрузки дампа в базу.</p>
<pre><code class="sql">mysql&gt; \. /home/vadim/dump.sql</code></pre>
<p><strong>8.  \e, edit<br />
</strong>Очень удобная команда, о которой я узнал недавно. Позволяет редакировать текущий SQL-запрос в системном редакторе (переменная окружения EDITOR).<br />
Небольшой хинт для тех, кто использует vim как стандартный редактор &#8211; добавляем в .vimrc такую строку:<br />
[code]au BufNewFile,BufRead *tmp/sql* set syntax=sql[/code]<br />
и в результате имеем ещё и подсветку синтаксиса SQL, которая при редактировании запроса включается автоматически.</p>
<p><strong>9. \! , system</strong><br />
Выполенение команда в системе не выходя из консоли mysql</p>
<pre><code class="sql">\! ls -la /home/vadim/*.sql</code></pre>
<p><strong>10. -U, &#8211;i-am-a-dummy<br />
</strong>Этот параметр консольного mysql, не позволяет выполнять обновления и удаления данных если в запросе не указан PRIMARY KEY. Обязательно насильно включать новичкам, а также тем, кто при выполнении DELETE/UPDATE запросов забывает писать оператор WHERE :)</p>
<p>Я специально тут не упямянул такие команды как <strong>use (\u)</strong>, <strong>rehash (\#)</strong>, <strong>clear (\c)</strong> и <strong>go (\g)</strong> так как считаю их общеизвестными и понятными абсолютно всем.<br />
Вот собственно и все, надеюсь эти советы будут полезны и используемы.<br />
Также буду признателен, если читатели поделятся своими mysql-related tips&amp;tricks.</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2007/10/19/mysql-client-secrets/feed/</wfw:commentRss>
		<slash:comments>24</slash:comments>
		</item>
		<item>
		<title>MySQL: As Small As Possible</title>
		<link>http://voituk.kiev.ua/2007/09/27/mysql-as-small-as-possible/</link>
		<comments>http://voituk.kiev.ua/2007/09/27/mysql-as-small-as-possible/#comments</comments>
		<pubDate>Thu, 27 Sep 2007 10:13:46 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://dev.voituk.kiev.ua/2007/09/27/mysql-as-small-as-possible/</guid>
		<description><![CDATA[Do you know how to create minimal storage table field? How to store single bit value in most optimized way? Just do it: [sql] CREATE TABLE dummy ( id int unsigned auto_increment primary key, ... bitField CHAR(0), # Only "" and NULL values ... ); SELECT bitField="" AS checked, ISNULL(bitField) AS unchecked FROM dummy; [/sql]]]></description>
			<content:encoded><![CDATA[<p>Do you know how to create minimal storage table field?<br />
How to store single bit value in most optimized way?</p>
<p>Just do it:<br />
[sql]
<pre>CREATE TABLE dummy (
  id int unsigned auto_increment primary key,
  ...
  bitField CHAR(0), # Only "" and NULL values
  ...
);

SELECT bitField="" AS checked, ISNULL(bitField) AS unchecked FROM dummy;</pre>
<p>[/sql]</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2007/09/27/mysql-as-small-as-possible/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>Записки ИТ-диверсанта: Как &#8220;сломать&#8221; MySQL-репликацию</title>
		<link>http://voituk.kiev.ua/2007/09/18/howto-break-mysql-replication/</link>
		<comments>http://voituk.kiev.ua/2007/09/18/howto-break-mysql-replication/#comments</comments>
		<pubDate>Tue, 18 Sep 2007 06:44:54 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://dev.voituk.kiev.ua/2007/09/18/howto-break-mysql-replication/</guid>
		<description><![CDATA[&#8230; или пособие о том, как не нужно делать :) Предположим у нас есть MySQL master-slave репликация, в которой мастер работает на MySQL 4.0, а все слейвы на MySQL 4.1 и выше. В принципе нормальная ситуация, т.к. в подобных системах мастер появляется первым, и возможно на тот момент версии MySQL 4.1 ещё попросту небыло. Теперь [...]]]></description>
			<content:encoded><![CDATA[<p><em>&#8230; или пособие о том, как не нужно делать :)</em></p>
<p>Предположим у нас есть MySQL master-slave репликация, в которой мастер работает на MySQL 4.0, а все слейвы на MySQL 4.1 и выше.</p>
<p>В принципе нормальная ситуация, т.к. в подобных системах мастер появляется первым, и возможно на тот момент версии MySQL 4.1 ещё попросту небыло.</p>
<p>Теперь выполняем  примитивный запрос:<span id="more-263"></span><br />
[sql]mysql&gt; CREATE TABLE mytable (id int unsigned not null default 0 auto_increment primary key)[/sql]<br />
В результате на всех слей-серверах &#8220;падает&#8221; репликация.</p>
<p>Почему?<br />
Дело в том, что начиная с версии 4.1, дабы стать ближе к стандартам SQL, было запрещено default-значение для auto-increment-оно поля.<br />
В результате на мастере запрос успешно выполняется, пишется в binlog, из которого он попадает на слейв-сервера, которые не могут его выполнить.</p>
<p>Посему правильно писать:<br />
[sql]mysql&gt; CREATE TABLE mytable (id int unsigned auto_increment primary key)[/sql]</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2007/09/18/howto-break-mysql-replication/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

