- shm/apc are 32-60 times faster than memcached or mysql
- shm/apc are 2 times faster than php file cache with apc
- php file cache with apc is 15-24 times faster than memcached or mysql
- mysql is 2 times faster than memcached when storing more than 400 bytes
- memcached is 2 times faster than mysql when storing less than 400 bytes
- php file cache with apc is 2-3 times faster than normal file cache
- php file cache without apc is 8 times slower than normal file cache
Tests were made with PHP 5.3.10, MySQL 5.5.29, memcached 1.4.13, 64bit, 3.4GHz (QEMU):
shm 0.031 0.020 0.021 0.021 0.026 0.028 0.032 0.042 0.084 0.155 0.290 0.629 0.110 Total: 1.489, Avg: 0.115 apc 0.025 0.025 0.025 0.026 0.031 0.036 0.043 0.060 0.106 0.171 0.328 0.756 0.097 Total: 1.728, Avg: 0.133 memcache 3.116 3.014 3.005 3.072 3.077 3.910 3.929 4.067 4.308 10.371 15.323 25.013 3.281 Total: 85.488, Avg: 6.576 memcache socket 1.736 1.756 1.981 1.780 1.809 1.907 1.941 1.983 2.225 9.368 14.071 24.897 1.979 Total: 67.435, Avg: 5.187 memcached 2.241 2.540 2.713 2.769 2.897 3.249 4.286 5.298 7.729 10.539 16.021 28.060 2.578 Total: 90.919, Avg: 6.994 mysql myisam 3.267 3.291 3.310 3.295 3.700 3.777 3.888 4.078 4.368 6.272 6.930 9.626 3.726 Total: 59.529, Avg: 4.579 mysql memory 3.238 3.360 3.470 3.502 3.310 3.346 3.681 4.108 4.370 6.286 7.279 7.079 3.397 Total: 56.426, Avg: 4.340 file cache 0.593 0.595 0.593 0.609 0.546 0.563 0.574 0.600 0.648 0.800 1.115 1.956 0.775 Total: 9.966, Avg: 0.767 php file cache 0.177 0.176 0.175 0.180 0.187 0.188 0.195 0.210 0.236 0.318 0.479 0.901 0.228 Total: 3.650, Avg: 0.281 (10b 0.1k 0.3k 0.5k 1k 2k 4k 8k 16k 32k 64k 128k array)Notes: Numbers in seconds, smaller numbers are better, connection times for memcached and mysql are not counted, file cache was done on tmpfs.
Cold cache, fill apc cache from cron:
All entries in apc cache are kept in shared memory. Shared memory is available to the web server and is not shared with the command line php (php-cli). When the web server is restarted, the cache is empty and refilling the complete cache can create performance problems. To fill the apc cache with a cron job, we use a second script to dump the cache to a binary file and load it inside the web server:
// php cron.php $data = array("cached"=>1, "hello"=>"world"); apc_store($data); apc_bin_dumpfile(array(), array_keys($data), "/var/cache/apc_cron.bin");
// bootstrap.php, http://myserver.com/... if (apc_fetch("cached")===false) apc_bin_loadfile("/var/cache/apc_cron.bin"); echo apc_fetch("hello"); // gives "world"
Related articles:
- MySQL 5.6+ InnoDB Memcached Plugin as a caching layer (from Mike Benshoof)
Here is the test script:
<?php // memcached -m 64 -s /tmp/m.sock -a 0777 -p 0 -u memcache // memcached -m 64 -l 127.0.0.1 -p 11211 -u memcache set_time_limit(3600); error_reporting(E_ALL); ini_set("display_errors", 1); ini_set("apc.enable_cli", 1); mysqli_report(MYSQLI_REPORT_STRICT | MYSQLI_REPORT_ERROR); $data = array( // test are range from 10-128,000 bytes "1111111110", str_repeat("1111111110", 10), str_repeat("1111111110", 30), str_repeat("1111111110", 50), str_repeat("1111111110", 100), str_repeat("1111111110", 200), str_repeat("1111111110", 400), str_repeat("1111111110", 800), str_repeat("1111111110", 1600), str_repeat("1111111110", 3200), str_repeat("1111111110", 6400), str_repeat("1111111110", 12800), array(0=>"1111111110", "id2"=>"hello world", "id3"=>"foo bar", "id4"=>42) ); echo "shm\n"; $t = array(); foreach ($data as $key=>$val) $t[] = shm(4000+$key, $val); echo stats($t); echo "apc\n"; $t = array(); foreach ($data as $key=>$val) $t[] = apc((string)$key, $val); echo stats($t); echo "memcache\n"; $t = array(); $m = memcache_connect("127.0.0.1", 11211); foreach ($data as $key=>$val) $t[] = memcache($m, (string)$key, $val); echo stats($t); echo "memcache socket\n"; $t = array(); $m = memcache_connect("unix:///tmp/m.sock", 0); foreach ($data as $key=>$val) $t[] = memcache($m, (string)$key, $val); echo stats($t); echo "memcached\n"; $t = array(); $m = new Memcached(); $m->addServer("127.0.0.1", 11211); foreach ($data as $key=>$val) $t[] = memcached($m, (string)$key, $val); echo stats($t); /* not in memcached 1.x echo "memcached socket\n"; $t = array(); $m = new Memcached(); $m->addServer("unix:///tmp/m.sock", 0); foreach ($data as $key=>$val) $t[] = memcached($m, (string)$key, $val); echo stats($t); */ echo "mysql myisam\n"; $t = array(); $m = new mysqli("127.0.0.1", "root", "", "t1"); mysqli_query($m, "drop table if exists t1.cache"); mysqli_query($m, "create table t1.cache (id int primary key, data mediumtext) engine=myisam"); foreach ($data as $key=>$val) $t[] = mysql_cache($m, $key, $val); echo stats($t); echo "mysql memory\n"; $t = array(); mysqli_query($m, "drop table if exists t1.cache"); mysqli_query($m, "create table t1.cache (id int primary key, data varchar(65500)) engine=memory"); foreach ($data as $key=>$val) $t[] = mysql_cache($m, $key, $val); echo stats($t); echo "file cache\n"; $t = array(); foreach ($data as $key=>$val) $t[] = file_cache((string)$key, $val); echo stats($t); echo "php file cache\n"; $t = array(); foreach ($data as $key=>$val) $t[] = php_cache((string)$key, $val); echo stats($t); function stats($t) { return "\nTotal: ".number_format(array_sum($t), 3).", ". "Avg: ".number_format(array_sum($t) / count($t), 3)."\n\n"; } function format($num) { return number_format($num, 3); } function shm($id, $data) { if (is_array($data)) { $arr = true; $data = serialize($data); } else $arr = false; $len = strlen($data); $shm_id = shmop_open($id, "c", 0644, $len); shmop_write($shm_id, $data, 0); $start = microtime(true); if ($arr) { for ($i=0; $i<100000; $i++) $v = unserialize(shmop_read($shm_id, 0, $len)); } else { for ($i=0; $i<100000; $i++) $v = shmop_read($shm_id, 0, $len); } echo format($end = microtime(true)-$start)." "; shmop_close($shm_id); assert(substr(is_array($v) ? $v[0] : $v, 0, 10)=="1111111110"); return $end; } function apc($id, $data) { apc_store($id, $data); $start = microtime(true); for ($i=0; $i<100000; $i++) $v = apc_fetch($id); echo format($end = microtime(true)-$start)." "; assert(substr(is_array($v) ? $v[0] : $v, 0, 10)=="1111111110"); return $end; } function memcache($m, $id, $data) { memcache_set($m, $id, $data); $start = microtime(true); for ($i=0; $i<100000; $i++) $v = memcache_get($m, $id); echo format($end = microtime(true)-$start)." "; assert(substr(is_array($v) ? $v[0] : $v, 0, 10)=="1111111110"); return $end; } function memcached($m, $id, $data) { $m->set($id, $data); $start = microtime(true); for ($i=0; $i<100000; $i++) $v = $m->get($id); echo format($end = microtime(true)-$start)." "; assert(substr(is_array($v) ? $v[0] : $v, 0, 10)=="1111111110"); return $end; } function mysql_cache($m, $id, $data) { $d = is_array($data) ? serialize($data) : $data; mysqli_query($m, "insert into t1.cache values (".$id.", '".$d."')"); $start = microtime(true); if (is_array($data)) { for ($i=0; $i<100000; $i++) { $v = mysqli_query($m, "SELECT data FROM t1.cache WHERE id=".$id)->fetch_row(); $v = unserialize($v[0]); } } else { for ($i=0; $i<100000; $i++) { $v = mysqli_query($m, "SELECT data FROM t1.cache WHERE id=".$id)->fetch_row(); } } echo format($end = microtime(true)-$start)." "; assert(substr($v[0], 0, 10)=="1111111110"); return $end; } function file_cache($id, $data) { file_put_contents($id, is_array($data) ? serialize($data) : $data); $start = microtime(true); if (is_array($data)) { for ($i=0; $i<100000; $i++) $v = unserialize(file_get_contents($id)); } else { for ($i=0; $i<100000; $i++) $v = file_get_contents($id); } echo format($end = microtime(true)-$start)." "; assert(substr(is_array($v) ? $v[0] : $v, 0, 10)=="1111111110"); return $end; } function php_cache($id, $data) { $id .= ".php"; $data = is_array($data) ? var_export($data, 1) : "'".$data."'"; file_put_contents($id, "<?php\n\$v=".$data.";"); touch($id, time()-10); // needed for APC's file update protection $start = microtime(true); for ($i=0; $i<100000; $i++) include($id); echo format($end = microtime(true)-$start)." "; assert(substr(is_array($v) ? $v[0] : $v, 0, 10)=="1111111110"); return $end; }