<?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>code.openark.org &#187; memcached</title>
	<atom:link href="http://code.openark.org/blog/tag/memcached/feed" rel="self" type="application/rss+xml" />
	<link>http://code.openark.org/blog</link>
	<description>Blog by Shlomi Noach</description>
	<lastBuildDate>Thu, 09 Sep 2010 16:15:02 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Using memcached functions for MySQL; an automated alternative to Query Cache</title>
		<link>http://code.openark.org/blog/mysql/using-memcached-functions-for-mysql-an-automated-alternative-to-query-cache</link>
		<comments>http://code.openark.org/blog/mysql/using-memcached-functions-for-mysql-an-automated-alternative-to-query-cache#comments</comments>
		<pubDate>Mon, 15 Dec 2008 05:56:14 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[Query Cache]]></category>
		<category><![CDATA[Triggers]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=89</guid>
		<description><![CDATA[There&#8217;s a lot of buzz around memcached. memcached is widely used, and has clients for many programming languages and platforms. TangentOrg have developed a memcached client in the form of MySQL UDFs (User Defined Functions). I wish to discuss the memcached functions for MySQL: if and how they should be used. Disclaimer: I do not [...]]]></description>
			<content:encoded><![CDATA[<p>There&#8217;s a lot of buzz around memcached. memcached is widely used, and has clients for many programming languages and platforms. <a href="http://tangent.org/">TangentOrg</a> have developed a memcached client in the form of MySQL UDFs (User Defined Functions).</p>
<p>I wish to discuss the memcached functions for MySQL: if and how they should be used.</p>
<p>Disclaimer: I do not work with memcached functions for MySQL on a production system; all that is written here reflects my opinion on how things should be done.</p>
<p>With memcached functions for MySQL, we can do the following:</p>
<blockquote>
<pre><strong>SELECT </strong>memc_set('mykey', 'The answer is 42');
<strong>SELECT </strong>memc_get('mykey');</pre>
</blockquote>
<p>(See my previous post on how to <a title="Installing memcached functions for MySQL" href="http://code.openark.org/blog/mysql/installing-memcached-functions-for-mysql">install memcached functions for MySQL</a>).</p>
<h4>In what scenario should we use these functions?</h4>
<p>I believe memcached is the right tool for the application level. I am less enthusiastic about using it from MySQL. Sure, pushing it down to MySQL centralizes everything. Instead of having all my application code (PHP, Java etc.) access memcached separately, they can all access one single MySQL node, which gets to access memcached. I see two problems with this approach:<span id="more-89"></span></p>
<ul>
<li>Doing this adds load on the database. I think the greatest advantage of memcached is that it allows us to alleviate load from the database. By pushing everything into MySQL we counter that benefit. We pay here both for loading the MySQL network and for the CPU consumed by MySQL to do the job. In a distributed application which used memcached, every server gets to take some of the load.</li>
<li>It seems to me as a flawed design. The database should be at an end point, and should not rely on anything except the operating system, file system and network. Sure, there could be applications talking to the database, but the database should be able to work all by itself. By putting memcached <em>behind</em> the database, we make the database dependent upon an external application.</li>
</ul>
<h4>How about memcached <em>increments</em>?</h4>
<p>memcached provides an increment mechanism, which can be used by MySQL to create distinct PRIMARY KEYs, like sequences in other databases. While this seems attractive, this feature fits most into the second point above: it makes MySQL completely dependant on memcached. So if memcached is down, MySQL is unable to generate keys.</p>
<h4>memcahced invalidation</h4>
<p>I believe a very good use would be to let MySQL invalidate cached data. Not set or get anything, just invalidate. To explain, let&#8217;s compare with MySQL&#8217;s query cache. I&#8217;ll be using <a title="MySQL's world database setup" href="http://dev.mysql.com/doc/world-setup/en/world-setup.html">MySQL&#8217;s world database</a>.</p>
<p>It is a known issue with the query cache, that if you change (INSERT/UPDATE/DELETE) data within a certain table, all queries involved with that table are invalidated. Take a look at the following:</p>
<blockquote>
<pre><strong>SELECT </strong>* <strong>FROM </strong>City <strong>WHERE </strong>CountryCode='BLZ';
<strong>UPDATE </strong>City <strong>SET </strong>Population=Population+1 <strong>WHERE </strong>CountryCode='CHE';
<strong>SELECT </strong>* <strong>FROM </strong>City <strong>WHERE </strong>CountryCode='BLZ';</pre>
</blockquote>
<p>The UPDATE does not affect the results for the SELECT query. Nevertheless, the second SELECT does not return from the query cache, since it&#8217;s invalidated by the UPDATE.</p>
<p>memcached can be used to solve this problem in a programmatic way. Let&#8217;s look at a short python program:<strong> memcached_test.py</strong>. What is does (see blue highlighted rows) is connect to memcached; connect to MySQL, and try to get the results for following from memcached:</p>
<blockquote>
<pre><strong>SELECT </strong>* <strong>FROM </strong>City <strong>WHERE </strong>CountryCode='BLZ';
<strong>SELECT </strong>* <strong>FROM </strong>City <strong>WHERE </strong>CountryCode='CHE';</pre>
</blockquote>
<p>If these results are in memcached, they are returned immediately. If not, they are retrieved from MySQL, then inserted into memcached. The results for &#8216;CHE&#8217; are under the &#8216;City:CHE&#8217; key, and &#8216;BLZ&#8217; is under &#8216;City:BLZ&#8217;.</p>
<blockquote>
<pre><strong>import </strong>MySQLdb
<strong>import </strong>memcache

<strong>def </strong>select_cities_by_country(country_code):
	key = <span style="color: #993300;"><strong>"City:"</strong></span>+country_code
	<span style="color: #3366ff;">cities = memcache_client.get(key)</span>
	<strong>if </strong>cities:
		found_in_memcached = <strong>True</strong>
	<strong>else</strong>:
		cursor = conn.cursor()
		cursor.execute(<span style="color: #993300;"><strong>"""
			SELECT Name, CountryCode,
			Population FROM City
			WHERE CountryCode=%s"""</strong></span>,
				country_code)
		<span style="color: #3366ff;">cities = cursor.fetchall()</span>
		<span style="color: #3366ff;">memcache_client.set(key, cities, 100)</span>
		cursor.close()
		found_in_memcached = <strong>False</strong>
	<strong>for </strong>row <strong>in </strong>cities:
		print <span style="color: #993300;"><strong>"%s, %s: %d"</strong></span> % (row[0], row[1], row[2])
	print <span style="color: #993300;"><strong>"%s found in memcached? %s\n"</strong></span> % (
                country_code, found_in_memcached)

conn = <strong>None</strong>
<strong>try</strong>:
	<strong>try</strong>:
		conn = MySQLdb.connect(
			host=<span style="color: #993300;"><strong>"localhost"</strong></span>,
                        user=<span style="color: #993300;"><strong>"myuser"</strong></span>,
			passwd=<span style="color: #993300;"><strong>"mypassword"</strong></span>,
			unix_socket=<span style="color: #993300;"><strong>"/tmp/mysql.sock"</strong></span>,
                        db=<span style="color: #993300;"><strong>"world"</strong></span>)
		memcache_client = memcache.Client([<span style="color: #993300;"><strong>"127.0.0.1:11211"</strong></span>])

		select_cities_by_country(<span style="color: #993300;"><strong>"BLZ"</strong></span>);
		select_cities_by_country(<span style="color: #993300;"><strong>"CHE"</strong></span>);
	<strong>except </strong>Exception, err:
		print err
<strong>finally</strong>:
	<strong>if </strong>conn:
		conn.close()</pre>
</blockquote>
<p>Let&#8217;s run this program. This is a first time run, so obviously nothing is in memcached:</p>
<blockquote>
<pre><strong>$ python memcached_test.py</strong>
Belize City, BLZ: 55810
Belmopan, BLZ: 7105
<strong>BLZ </strong>found in memcached? <strong>False</strong>

Zurich, CHE: 336800
Geneve, CHE: 173500
Basel, CHE: 166700
Bern, CHE: 122700
Lausanne, CHE: 114500
<strong>CHE </strong>found in memcached? <strong>False</strong></pre>
</blockquote>
<p>Immediately executed again, we get results from memcached:</p>
<blockquote>
<pre><strong>$ python memcached_test.py</strong>
Belize City, BLZ: 55810
Belmopan, BLZ: 7105
<strong>BLZ </strong>found in memcached? <strong>True</strong>

Zurich, CHE: 336800
Geneve, CHE: 173500
Basel, CHE: 166700
Bern, CHE: 122700
Lausanne, CHE: 114500
<strong>CHE </strong>found in memcached? <strong>True</strong></pre>
</blockquote>
<p>We are going to execute the following query:</p>
<blockquote>
<pre><strong>UPDATE </strong>City <strong>SET </strong>Population=Population+1 <strong>WHERE </strong>CountryCode='CHE';</pre>
</blockquote>
<p>But nothing as yet will invalidate our memcached values. Let&#8217;s set up TRIGGERs on the City table:</p>
<blockquote>
<pre><strong>DELIMITER </strong>$$

<strong>DROP TRIGGER IF EXISTS</strong> City_AI $$
<strong>CREATE TRIGGER</strong> City_AI <strong>AFTER INSERT ON</strong> City
<strong>FOR EACH ROW
BEGIN
  SELECT</strong> memc_delete(<strong>CONCAT</strong>('City:',<strong>NEW</strong>.CountryCode)) <strong>INTO </strong>@discard;
<strong>END</strong>;
$$

<strong>DROP TRIGGER IF EXISTS</strong> City_AU $$
<strong>CREATE TRIGGER</strong> City_AU <strong>AFTER UPDATE ON</strong> City
<strong>FOR EACH ROW
BEGIN
  SELECT</strong> memc_delete(<strong>CONCAT</strong>('City:',<strong>OLD</strong>.CountryCode)) <strong>INTO </strong>@discard;
  <strong>SELECT </strong>memc_delete(<strong>CONCAT</strong>('City:',<strong>NEW</strong>.CountryCode)) <strong>INTO </strong>@discard;
<strong>END</strong>;
$$

<strong>DROP TRIGGER IF EXISTS</strong> City_AD $$
<strong>CREATE TRIGGER</strong> City_AD <strong>AFTER DELETE ON</strong> City
<strong>FOR EACH ROW
BEGIN
  SELECT</strong> memc_delete(<strong>CONCAT</strong>('City:',<strong>OLD</strong>.CountryCode)) <strong>INTO </strong>@discard;
<strong>END</strong>;
$$

<strong>DELIMITER </strong>;</pre>
</blockquote>
<p>These triggers will cause any change to a city invalidates all cities in the same country. Naive? Far less than MySQL&#8217;s query cache. Let&#8217;s put this to the test:</p>
<blockquote>
<pre>mysql&gt; <strong>UPDATE </strong>City <strong>SET </strong>Population=Population+1 <strong>WHERE </strong>CountryCode='CHE';
Query OK, 5 rows affected (0.01 sec)
Rows matched: 5  Changed: 5  Warnings: 0</pre>
</blockquote>
<p>And run out python program one last time:</p>
<blockquote>
<pre><strong>$ python memcached_test.py</strong>
Belize City, BLZ: 55810
Belmopan, BLZ: 7105
<strong>BLZ </strong>found in memcached? <span style="color: #339966;"><strong>True</strong></span>

Zurich, CHE: 336801
Geneve, CHE: 173501
Basel, CHE: 166701
Bern, CHE: 122701
Lausanne, CHE: 114501
<strong>CHE </strong>found in memcached? <span style="color: #339966;"><strong>False</strong></span></pre>
</blockquote>
<p>Right! The &#8216;CHE&#8217; values were invalidated, and could not be found in memcaches. &#8216;BLZ&#8217;, however, wasn&#8217;t disturbed.</p>
<p>We can further improve our invalidation mechanism to check only for changes for desired columns. This will require some more code in our triggers.</p>
<h4>Notes</h4>
<p>The triggers themselves pose a performance penalty on our code. It is assumed that SELECTs are more important here, or else we would not use caching at all. At any case, the example provided here has not been benchmarked, and its value can only be estimated in your real life situation.</p>
<h4>Conclusion</h4>
<p>I believe invalidation is the most interesting part of memcached functions for MySQL. It makes the most sense:</p>
<ul>
<li>No data passes between MySQL and memcached.</li>
<li>The application isn&#8217;t even aware that MySQL is talking to memcached. MySQL does everything internally using triggers.</li>
<li>MySQL does not depend on memcached. If memcached goes away, the triggers will simply have no effect. It is still possible that due to temporary network failure, an invalidation is skipped. But memcached supports us by adding a timeout for cached values, so we have some kind of &#8220;backup plan&#8221;.</li>
</ul>
<p>Please share below your insights and real life experience with memcached functions for MySQL.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/using-memcached-functions-for-mysql-an-automated-alternative-to-query-cache/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Installing memcached functions for MySQL</title>
		<link>http://code.openark.org/blog/mysql/installing-memcached-functions-for-mysql</link>
		<comments>http://code.openark.org/blog/mysql/installing-memcached-functions-for-mysql#comments</comments>
		<pubDate>Wed, 10 Dec 2008 07:56:30 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[memcached]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=316</guid>
		<description><![CDATA[tangent.org provide a memcached client for MySQL, in the form of UDFs. The provided set of functions allow for connecting to a memcached server, putting values in the cache, getting values, invalidating, utilizing increments etc.

The code is not (yet?) available in binary format, so the libraries need to be compiled and installed manually. Following is a quick installation HOWTO for Linux users.]]></description>
			<content:encoded><![CDATA[<p><a href="http://tangent.org/">tangent.org</a> provide a memcached client for MySQL, in the form of UDFs. The provided set of functions allow for connecting to a memcached server, putting values in the cache, getting values, invalidating, utilizing increments etc.</p>
<p>The code is not (yet?) available in binary format, so the libraries need to be compiled and installed manually. Following is a quick installation HOWTO for Linux users.<span id="more-316"></span></p>
<p>I have installed on Ubuntu 8.04, running on an old IBM ThinkPad (600X) which has ~490MB and 10GB of disk space, with a 500MHz who-knows-which Intel processor. Well, that&#8217;s the machine on which I experiment&#8230; The setup I&#8217;ve tested is a single machine setup, with a single memcached instance.</p>
<h4>memcached</h4>
<p>To start with, you need to have the <a href="http://www.danga.com/memcached/">memcached</a> daemon installed. Easy enough:</p>
<blockquote>
<pre>sudo apt-get install memcached</pre>
</blockquote>
<p>That should install memcached and start the daemon, on the default 11211 port (see /etc/memcached.conf)</p>
<h4>libmemcached</h4>
<p>Next, memcached functions for MySQL depend on <a href="http://tangent.org/552/libmemcached.html">libmemcached</a>. This one comes with RPM and SRPM builds, but I&#8217;m running on Ubuntu/Debian, which invites trouble: I&#8217;ve tried installing the RPM, but got into dependency hell. I thought I may as well just compile the sources. And so I&#8217;ve downloaded libmemcached-0.25.tar.gz, and went throught the usualy steps:</p>
<blockquote>
<pre>tar xzfv libmemcached-0.25.tar.gz
cd libmemcached-0.25/
./configure
make
sudo make install</pre>
</blockquote>
<p>The configure script did give me some trouble, claiming something about invalid struct padding. Running configure with</p>
<blockquote>
<pre>bash -x configure</pre>
</blockquote>
<p>has shown that I was simply missing the g++ compiler. Once installed, all went well.</p>
<h4>MySQL</h4>
<p>We do need to have MySQL up and running, of course. Required version is 5.0 and above. But we also need to have <strong>mysql_config</strong>. This tool does not come with the standard apt-get package for debian/ubuntu. It is available in the develop package, though:</p>
<blockquote>
<pre>sudo apt-get install libmysqlclient-dev</pre>
</blockquote>
<p>RedHat and derived users can use the <strong>mysql-devel</strong> RPM. I have MySQL installed from binary tarball, so <strong>mysql_config</strong> is already there.</p>
<h4>More dependencies</h4>
<p>The README states you need to have the latest <strong>autoconf</strong> tools. <strong>pkg-config</strong> was required.</p>
<h4>memcached funtions for MySQL</h4>
<p>Finally, we get to business. Download sources for <a href="http://tangent.org/586/Memcached_Functions_for_MySQL.html">memcached functions to MySQL</a>.</p>
<blockquote>
<pre>tar xzfv memcached_functions_mysql-0.7.tar.gz
cd memcached_functions_mysql-0.7/
./configure --with-mysql=/usr/local/mysql/bin/mysql_config --libdir=/usr/local/mysql/lib/mysql
make
sudo make install</pre>
</blockquote>
<p>The parameters to <strong>configure</strong> are the location of <strong>mysql_config</strong>, and the destination into which the libraries are written. Since I&#8217;ve installed my MySQL tarball on <strong>/usr/local/mysql</strong>, my destination is <strong>/usr/local/mysql/lib/mysql</strong>. You may wish to set this one up differently. Once the process is done, see that the libraries have indeed been created there. In particular, you&#8217;re looking for <strong>libmemcached_functions_mysql.so</strong>.</p>
<p>These libraries need to be found in the library search path. One way of doing so is to add the path to <strong>/etc/ld.so.conf</strong>:</p>
<blockquote>
<pre>sudo echo /usr/local/mysql/lib/mysql/ &gt;&gt; /etc/ld.so.conf</pre>
</blockquote>
<p>And then update the search path</p>
<blockquote>
<pre>sudo ldconfig</pre>
</blockquote>
<p>Once this is done, we can install the functions in MySQL. Go to the <strong>memcached_functions_mysql-0.7/</strong> path, and execute:</p>
<blockquote>
<pre>mysql -u root -p &lt; sql/install_functions.sql</pre>
</blockquote>
<p>This file simply contains the <strong>CREATE FUNCTION</strong> statements for all supplied memcached API.</p>
<h4>Testing</h4>
<p>To put our installation to the test, let&#8217;s try setting a value to memcached, then getting it back:</p>
<blockquote>
<pre>mysql&gt; SELECT memc_set('mykey', 'Getting this with SELECT means all works well');
+--------------------------------------------------------------------+
| memc_set('mykey', 'Getting this with SELECT means all works well') |
+--------------------------------------------------------------------+
|                                                                  0 |
+--------------------------------------------------------------------+
1 row in set (0.03 sec)

mysql&gt; SELECT memc_get('mykey');
+-----------------------------------------------+
| memc_get('mykey')                             |
+-----------------------------------------------+
| Getting this with SELECT means all works well |
+-----------------------------------------------+
1 row in set (0.00 sec)</pre>
</blockquote>
<p>The README file contains examples for all supplied functions. Take a look at the <a title="MySQL docs" href="http://dev.mysql.com/doc/refman/5.0/en/ha-memcached-interfaces-mysqludf.html">MySQL docs</a>, as well.</p>
<h4>Conclusion</h4>
<p>It is also nice to see that Java or Python clients are also able to read the value stored with the &#8220;mykey&#8221; key. Well, that&#8217;s the nice thing about memcached: its diversity and compatibility of clients.</p>
<p>In a future post, I will write about if, why and how I think memcahed functions for MySQL should be used.</p>
<p>Please share below any insights about installing on other Linux flavours, BSD, Solaris or other operating systems.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/installing-memcached-functions-for-mysql/feed</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
	</channel>
</rss>
