基于sql实现redis主动缓存

主要功能点

主动缓存,无异常情况下只从redis中取数据,不走mysql
列表的分类、排序
缓存数据中区分正在进行、未开始、已结束
分析sql语句触发redis缓存系统中数据的更新
对关联数据进行同步更新(例 广告中的数据里面有商品信息,当商品发生改变时,触发把广告中的相关商品数据内容也进行同步更新)
针对单个表的缓存数据重建功能

继续阅读

nginx配置PATH_INFO并rewrite掉中间的index.php

最近看博客统计数据,还是有很多搜索nginx 配置 PATH_INFO 和去掉中间的index.php的关键词,所以把目前自己在用的配置传一份到网上

server
{
listen 80;
server_name ye55.dev;
index index.html index.htm index.php default.html default.htm default.php;
root /home/www/ye55/trunk/www/;

if (!-f $request_filename) {
rewrite ^/(.*)$ /index.php/$1 last;
}
location ~ .*\.php(.*)$
{
fastcgi_pass unix:/tmp/php-cgi.sock;
fastcgi_index index.php;
include fcgi.conf;
fastcgi_split_path_info ^(.+\.php)(.*)$;
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

基于分析sql语句实现主动缓存

以前在上海的时候,做过一个用redis实现主动列表缓存的方案,但那时生产环境用的是1.0,所以那个列表缓存非常粗糙,只是满足需求而以,但运行得比较稳定,因此redis在实现上是可行的。

加上最近在看redis方面的资料,从头系统过了一遍redis,所以有了改进以前做的主动列表缓存的冲动,计划首先用在现在公司的一个新项目上,如果可行,就可以继续部署到老项目,改进性能。

要实现主动缓存,主要的问题在于以下几点

1. 怎么触发更新、删除、插入数据库时,同步更新redis里的数据

2. redis中数据的存储采用怎么的方式

3. 主动缓存中怎么排序和分类

4. redis意外停止服务的情况下,如果正常提供列表服务

5. 列表缓存应该工作在哪一层,dao ? service ?

6. redis中单个数据失效的情况下怎么剔除

7. 如果减少网络请求,尽量少的命令获取一个分布的数据

目前方案正在设想中,先写下些东西,做下记录,后面再逐步完善!

一,解决mysql数据改变时触发实时更新redis数据,并最少改动现有代码

所以想在加一层 cache 层,使用cache层可以在Controller和service二个层中相互调用。cache中的数据可以来源于dao也可以来源于service层

解决第一个问题,我的想法是,直接在sql执行时,获得sql语句分析sql , 决定是否更新redis中的数据。

二, 做要实现redis主动缓存的相关配置,配置如下

<?php

return array(
			// db_user 表做数据缓存
			'db_user' => array(
							// 定义分类的字段,用于生成多个id索引set
							'cate' => array('group_id', 'vip'),
							// 设定排序所要用到的字段,数字
							'sort' => array('sort', 'create_time', 'last_time', 'login_num'),
							'callback' => array(
												'get' => array('common::getUserService()', 'getOne'), //回调类与方法,用于更新单个数据
												//用于当redis数据丢失的情况从mysql中还原数据,需要用于反射来注入数据,有待完善
												'getlist' => array('common::getUserService()', 'getList'), 
											), 

						),
		);

主要的意义在配置中告诉程序怎么输出,如果redis失效的情况下,绕过redis缓存系统,直接按回调中的方法从mysql中输出数据。

redis怎么存储缓存数据:

1. 用一个或多个sets 存 id号索引数据。 比如配置中cate字段没有设置,就类型 listcache:db_user:ids

如果配置了cate字段 则出现一组 listcache:db_user:ids:cate:group_id:1 sets来分别存放对应的ID号

2. 另使用一个hash来存储内容,结构因sort配置而变
hset list:cache:db_user:content:id:1 sort 1
hset list:cache:db_user:content:id:1 create_time 122323123
hset list:cache:db_user:content:id:1 last_time 1223231223
hset list:cache:db_user:content:id:1 login_num 20
hset list:cache:db_user:content:id:1 data 用户数据的序列化数据
上面非data用于排序使用

最后获取列表使用 sort 命令
例 sort list:cache:ids BY list:cache:db_user:content:id:*->sort DESC LIMIT 0 10
上面命令用于获取排过序id号数据
也可以直接获取最终的data数据 sort list:cache:ids BY list:cache:db_user:content:id:*->sort GET list:cache:db_user:content:id:*->data DESC LIMIT 0 10

就写到这吧,写得比较混乱。以后再整理些图出来,比较直观!

php路由实现rewrite改写url

最近着手写一个网站,从框架到应用全部重新开发,很多代码属于重造轮子,但主要的目的就是练手,因为发现最近思维有些固化了,是得好好从头到底写个项目了!

以前写的框架中路由功能非常有限,只是实现controller与action的选择,代码很简陋,没做过滤,安全性也有问题,所以就重写了一个路由。

框架出错提示

主要功能:

  1. controller与action的选择
  2. php正则rewrite美化url
  3. 参数过滤
  4. PATH_INFO与REQUEST_URI自动选择

继续阅读

memcache一致性hash的php实现

有段时间没认真写博客了,最近在看一些分布式方面的文章,所以就用php实现一致性hash来练练手,以前一般用的是最原始的hash取模做分布式,当生产过程中添加或删除一台memcache都会造成数据的全部失效,一致性hash就是为了解决这个问题,把失效数据降到最低,相关资料可以google一下!

php实现效率有一定的缺失,如果要高效率,还是写扩展比较好
经测试,5个memcache,每个memcache生成200个虚拟节点,set加get1000次,采用一致性哈希分布效率比原生单台速度相差5倍,效率有待优化
实现过程:

  1. memcache的配置 ip+端口+虚拟节点序列号 做hash,使用的是crc32,形成一个闭环。
  2. 对要操作的key进行crc32
  3. 二分法在虚拟节点环中查找最近的一个虚拟节点
  4. 从虚拟节点中提取真实的memcache ip和端口,做单例连接

代码如下: 继续阅读

redis做共享锁机制

给出部分主代码

感谢HaKeem的提醒,程序发现一个bug,以前的代码会发生无法解锁的问题,放出修正后的代码
如果是redis2.2以上版本可以在lock逻辑中加上watch命令,锁定单个key

	/**
	 * 加锁  【新的加锁算法,采用redis做锁,redis失效情况下,返回 变量redisErrReturn 设定的值】
	 * @param string $key 唯一标识
	 * @param int $expire
	 * @return
	 */
	public function lock($key, $expire = 5) {
		try {
			list($key, $lockIdKey) = $this->_getLockKey($key);
			//抢锁,第一个线程抢到,并把过期时间写入锁
			if (Common::getQueue()->setnx($key, Common::getTime() + $expire)) return true;
			//没有抢到锁的线程,判断锁是否异常死锁,如果锁没有过期,返回false
			if (Common::getQueue()->get($key)  > Common::getTime()) return false;
			//锁因异常死锁并过期的情况下,多个并发线程再次抢锁,getset命令到过期时间,如果未过期,表示锁已被其它线程抢得,返回false
			if (Common::getQueue()->getset($key, Common::getTime() + $expire) > Common::getTime()) return false;
			Common::getQueue()->set($lockIdKey, $this->_lockId);
			return true;
		} catch (RedisException $e) {
			// 当捕捉到redis异常时,锁中断,返回变量redisErrReturn
			$this->_lockStatus = false;
			return $this->redisErrReturn;
		}
	}
	
	/**
	 * 给锁进行续期,延长锁的生效周期
	 * @param string $key
	 * @param int $expire       延时锁失效的秒数
	 * @param int $triggerTime  触发续期倒计时间
	 * @return
	 */
	public function updateExpire($key, $expire = 3, $triggerTime = 2) {
		if (!$this->_lockStatus) return true; //当redis失效,中断锁
		list($key, $lockIdKey) = $this->_getLockKey($key);
		$lockId = Common::getQueue()->get($lockIdKey);
		if ($this->_lockId === null || $lockId != $this->_lockId) return false;
		$time = Common::getQueue()->get($key);
		if ($time - $triggerTime <= Common::getTime()) return Common::getQueue()->set($key, $time + $expire);
	}
	
	/**
	 * 解锁
	 * @param string $key
	 * @return
	 */
	public function unlock($key) {
		if (!$this->_lockStatus) return true; //当redis失效,中断解锁
		list($key, $lockIdKey) = $this->_getLockKey($key);
		$lockId = Common::getQueue()->get($lockIdKey);
		if ($this->_lockId === null || $lockId != $this->_lockId) return false;
		Common::getQueue()->del($key);
		Common::getQueue()->del($lockIdKey);
		return true;
	}
	
	/**
	 * 清除死锁产生的无用key 默认清除24小时以前的无用key
	 * @param int $cleanTime  默认24小时
	 */
	public function cleanLockKey($cleanTime = 86400) {
		if ($cleanTime < 3600 || !$keys = Common::getQueue()->keys('PwLock:*')) return false;
		$i = 0;
		foreach ($keys as $value) {
			list($time, $lockId) = explode('|', Common::getQueue()->get($value));
			if (Common::getTime() - $time > $cleanTime) {
				if (Common::getQueue()->del($value)) $i++;
			}
		}
		return $i;
	}
	
	private function _getLockKey($key) {
		return array('PwLock:' . $key, 'PwLock:' . $key . ':lockId');
	}

linux tar分卷压缩

分卷压缩一个目录:如linux
在linux目录的上层目录:
#tar cvf linux|split -b 2m (已2M大小分卷压缩)
#cat x* > linux.tar (合成分卷压缩包)
或者
#tar czvf linux.tar.gz linux/
#tar czvfp – linux.tar.gz | split -b 2m
#cat x* > linux.tar.gz

Mac OS Lion 下编译安装Nginx 1.0.12 + PHP 5.3.10 + Mysql 5.5.18 + Xdebug + PHPUnit [转]

在mac下编译安装了最新版本namp的环境, 记录一下安装过程, 其实总体和linux下没有太多区别,另外port是个好东西。

mysql编译安装:

1. 建立mysql数据存储目录, 权限设置为mac os默认存在的_mysql权限:

mkdir -p /var/mysql/data/
chown -R _mysql:_mysql /var/mysql/

2. 下载mysql, 我下的版本为mysql-5.5.18, 执行以下跨平台编译命令:

cmake -DCMAKE_INSTALL_PREFIX=/usr/local/server/mysql -DMYSQL_DATADIR=/var/mysql/data -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_MEMORY_STORAGE_ENGINE=1 -DWITH_MYISAM_STORAGE_ENGINE=1 -DSYSCONFDIR=/etc/ -DWITH_SSL=yes -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_READLINE=on

3. 编译, 安装:

make
sudo make install

4. 改变mysql目录的权限:

sudo chmod +w /usr/local/server/mysql
sudo chown -R _mysql:_mysql /usr/local/server/mysql

5. 创建库软链接:

sudo ln -s /usr/local/server/mysql/lib/lib* /usr/lib/

6.  copy配置文件到etc目录:

cp /usr/local/server/mysql/support-files/my-large.cnf /etc/my.cnf
[client]
default-character-set = utf8

[mysqld]
character-set-server = utf8
default-storage-engine = MyISAM
basedir = /usr/local/server/mysql
datadir = /var/mysql/data
log-error = /var/mysql/mysql_error.log
pid-file = /var/mysql/mysql.pid

7. 建立初始数据表:

sudo /usr/local/server/mysql/scripts/mysql_install_db --basedir=/usr/local/server/mysql --datadir=/var/mysql/data --user=_mysql

8. 设置root密码:

sudo /usr/local/server/mysql/bin/mysqladmin -u root password 'mysql'

9.启动mysql:

sudo /usr/local/server/mysql/bin/mysqld_safe --user=_mysql &

10. 测试安装是否成功:

/usr/local/server/mysql/bin/mysql -u root -p -S /tmp/mysql.sock

 

php编译安装:

1. 编译安装php:

./configure –prefix=/usr/local/server/php –with-config-file-path=/usr/local/server/php/etc –enable-fpm –with-openssl –with-zlib –enable-mbstring –with-mcrypt –with-mysql=/usr/local/server/mysql –with-mysql-sock=/tmp/mysqld.sock –with-mysqli=/usr/local/server/mysql/bin/mysql_config –enable-sockets –without-iconv –with-curl=/opt/local/bin/curl

2. 复制php.ini-development到编译时指定的php配制目录:

sudo cp php.ini-development /usr/local/server/php/etc/php.ini

3. 复制phpfpm的配置文件到其配制目录:

sudo cp php-fpm.conf.default php-fpm.conf

4. 重命名phpfpm执行文件为正常名称:

sudo mv php-fpm.dSYM php-fpm

5. 启动:

sudo /usr/local/server/php/sbin/php-fpm

6. 安装xdebug

1) 进入http://xdebug.org/find-binary.php网址, 输入phpinfo返回的html源码后其会自动生成安装步骤, 按照其方法编译安装, 最后变更php.ini配制文件指定xdebug.so扩展路径。

7. 安装phpunit

1) 升级pear.

sudo /usr/local/server/php/bin/pear upgrade

2) 安装phpunit.

sudo ./pear config-set auto_discover 1
sudo ./pear install pear.phpunit.de/PHPUnit

3) 测试phpunit是否安装成功.

/usr/local/server/php/bin/phpunit

#有输出帮助信息则为正常。


nginx编译安装:

1. 编译安装:

./configure --user=_www --group=_www --prefix=/usr/local/server/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module
make
sudo make install

2. 修改nginx配置:

sudo vim /usr/local/server/nginx/conf/nginx.conf

#指定程序运行权限:
user _www _www;
#在http内添加一条server信息:
server {
    listen 80; 
    server_name localhost;
    root /var/www;

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
        include fastcgi_params;
    } 
}

3.启动:

sudo /usr/local/server/nginx/sbin/nginx

#没有任何返回则说明执行成功。

 

参考文章:

lnap最新方案   http://www.yunwei8.com/nginx/

xcode4.3开启gcc/g++

真的坑爹,今天才开始玩MAC OX,装了个最新版本的10.7.3,只能装XCODE 4.3 这个月刚发行的版本。
安装时发现没有install过程,直接双击就进入开发环境了。而且装完后没有gcc 等各种编译工具,在TERMINAL下各种命令不识别,想装ruby的各种开发工具,都不行了。

查了半天才发现:
Apple announced Xcode 4.3 for OSX Lion and 4.4 for OSX Mountain Lion last week. The major difference is that Xcode no longer provide an installer which is good thing because you now could update Xcode with AppStore in the future, plus it is much easier to carry the development environment with you. However, there is a little problem with this new version of Xcode, is that all command line toolsets and compilers are not visible in terminal.

解决方案一:
A simple fix is to update your PATH env:
export PATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:$PATH
Please be noted that clang does not reside in /Developer/usr/bin, it is now in /Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Now you could access to gcc, g++, git or any toolsets bundled with Xcode. For your convenience, it is recommended to include it in your .bash_profile.
解决方案二:
You can install these additional tools directly in Xcode :
Preferences > Downloads > Command Line Tools > Install