登录 |

emlog优化(一)——罪恶的缓存

2013年09月28日 下午 56:01 | 作者:

引言

emlog使用文件缓存,将数据序列化后存放到 content/cache/,如果你的blog只有几百篇日志,那么这个缓存还是有效果的,如果用emlog运营商业化应用,添加个上千篇日志,这个缓存设计就反倒成为拖累系统的累赘。

问题原因

来看看 include/lib/cache.php 是怎么读取缓存的:

function readCache($cacheName) {
	if ( $this->{$cacheName.'_cache'} == null ) {
		$cachefile = EMLOG_ROOT . '/content/cache/' . $cacheName . '.php';
		// 如果缓存文件不存在则自动生成缓存文件
		if (!is_file($cachefile) || filesize($cachefile) <= 0) {
				if (method_exists($this, 'mc_' . $cacheName)) {
						call_user_func(array($this, 'mc_' . $cacheName));
				}
		}
		if ($fp = fopen($cachefile, 'r')) {
				$data = fread($fp, filesize($cachefile));
				fclose($fp);
				$this->{$cacheName.'_cache'} = unserialize(str_replace("{$cacheName.'_cache'};
}

fopen文件,str_replace替换保护前缀,然后unserialize数据;
来看看一个8000条日志的blog,产生多少缓存数据:

ls -lhS content/cache/
total 17M
-rw-r--r-- 1 www-data www-data 3.9M Sep 28 23:21 logtags.php
-rw-r--r-- 1 www-data www-data 3.1M Sep 28 23:21 tags.php
-rw-r--r-- 1 www-data www-data 693K Sep 28 23:21 logsort.php
-rw-r--r-- 1 www-data www-data 434K Sep 28 23:21 logalias.php
-rw-r--r-- 1 www-data www-data 7.3K Sep 28 23:21 options.php
-rw-r--r-- 1 www-data www-data 2.7K Sep 28 23:21 record.php
-rw-r--r-- 1 www-data www-data 1.4K Sep 28 23:21 newtw.php
-rw-r--r-- 1 www-data www-data 1.1K Sep 28 23:21 comment.php
-rw-r--r-- 1 www-data www-data  971 Sep 28 23:21 sort.php
-rw-r--r-- 1 www-data www-data  935 Sep 28 23:21 logatts.php
-rw-r--r-- 1 www-data www-data  829 Sep 28 23:21 link.php
-rw-r--r-- 1 www-data www-data  691 Sep 28 23:21 newlog.php
-rw-r--r-- 1 www-data www-data  679 Sep 28 23:21 user.php
-rw-r--r-- 1 www-data www-data  673 Sep 28 23:21 sta.php
-rw-r--r-- 1 www-data www-data  592 Sep 28 23:21 navi.php

缓存文件最大达到了3.9M,反序列化时间长达300~500ms,所以说嘛,不会设计缓存,还不如老老实实用数据库SELECT;

优化

优化的方向?

  1. 更快的反序列化方法,比如json、igbinary、msgpack
  2. 对象持久化:hidef

我选用hidef持久化的方法,在php-fpm启动时一次性载入大数组,每次进程请求直接引用数据对象,无需反序列化。

  1. 安装hidef
    pecl install hidef
  2. 配置hidef
    extension=hidef.so
    [hidef]
    hidef.data_path=/var/www/emlog/content/cache
  3. 修改  include/lib/cache.php
            /**
             * 写入缓存
             */
            function cacheWrite ($cacheData, $cacheName) {
                    // 为hidef写入cache,文件后缀名必须为.data
                    @file_put_contents(EMLOG_ROOT . '/content/cache/' . $cacheName . '.data', $cacheData);
                   // 原写入缓存代码 从略 ……
            }
    
            /**
             * 读取缓存文件
             */
            function readCache($cacheName) {
                    if ( $this->{$cacheName.'_cache'} == null ) {
                            if ( strpos($_SERVER['REQUEST_URI'], 'admin') === false && // 管理后台不使用hidef加速
                                    $cacheVal = hidef_fetch($cacheName) ) { // hidef_fetch 获取缓存目录下对应 ***.data 的数据
                                    if ( in_array($cacheName, array('options')) ) {
                                            $cacheVal = $cacheVal->thaw();
                                    }
                                    $this->{$cacheName.'_cache'} = $cacheVal;
                            } else {
                                    // 原读取缓存代码 从略 ……
                            }
                    }
    
                    return $this->{$cacheName.'_cache'};
            }
  4. Crontab 监控缓存目录更新,自动重载php5-fpm
    * * * * * /usr/local/bin/php5-fpm-reload 

    #!/bin/sh
    # php5-fpm-reload
    md5_new="`ls -l /var/www/emlog/content/cache | md5sum | cut -d ' ' -f1`"
    md5_old="`cat /tmp/cache.md5`"
    
    if [ "$md5_new" != "$md5_old" ]; then
            service php5-fpm reload
            echo $md5_new > /tmp/cache.md5
    fi
  5. 由于hidef将数组作为FrozenArray引用对象保存,这个对象ReadOnly,支持key读取,foreach迭代,不能赋值,unset,不能使用数组函数array_XXX 操作,可以使用thaw()导出为标准Array; emlog大量用到array_search, 写个自定义函数替换掉:
    function _array_search( $needle, $haystack, $strict=false) {
            if ( is_array($haystack) ) return array_search($needle, $haystack, $strict);
            foreach ( $haystack as $_key => $_val ) {
                    $find = $strict ? ($_val === $needle) : ($_val == $needle);
                    if ($find) return $_key;
            }
    }

优化结果

优化前 优化后 Diff Diff%
Incl. Wall Time(microsec)
执行时间(毫秒)
376,113
376 毫秒
8,921
8毫秒
-367,192 -97.6%
Incl.
PeakMemUse(bytes)
峰值内存占用
67,770,496
64Mb
522,056
0.5Mb
-67,248,440 -99.2%

关键路径优化前/后的时间差

mysql挂载data到zfs分区

2013年09月12日 上午 43:34 | 作者:

zpool create tank /dev/sdb1

rm -rf /var/lib/mysql

zfs create -o mountpoint=/var/lib/mysql tank/mysql

mysql_install_db

chcon -R -t mysqld_db_t /var/lib/mysql

/etc/my.conf
innodb_use_native_aio=false

mysql start

 

Oracle Solaris 管理:ZFS 文件系统
http://docs.oracle.com/cd/E26926_01/html/E25826/

ZFS in 30 minutes

SElinux 安全策略 引发的mysql数据库无法启动 http://blog.sina.com.cn/s/blog_53b13d950100w4yt.html
chcon -R -t mysqld_db_t /var/lib/mysql

使用tmpfs文件系统做MySQL tmpdir潜在的问题 http://www.linuxidc.com/Linux/2013-03/80696.htm
/etc/my.conf
innodb_use_native_aio=false