phpredis操作
A. php涓轰粈涔堜娇鐢╮edis
1銆 Redis绠浠
redis鏄疦osql鏁版嵁搴扑腑浣跨敤杈冧负骞挎硾镄勯潪鍏崇郴鍨嫔唴瀛樻暟鎹搴掳纴redis鍐呴儴鏄涓涓猭ey-value瀛桦偍绯荤粺銆傚畠鏀鎸佸瓨鍌ㄧ殑value绫诲瀷鐩稿规洿澶氾纴鍖呮嫭string(瀛楃︿覆)銆乴ist(阈捐〃)銆乻et(闆嗗悎)銆亃set(sorted set _链夊簭闆嗗悎)鍜宧ash锛埚搱甯岀被鍨嬶纴绫讳技浜嶫ava涓镄刴ap锛夈俣edis锘轰簬鍐呭瓨杩愯屽苟鏀鎸佹寔涔呭寲镄凬oSQL鏁版嵁搴掳纴鏄褰揿墠链鐑闂ㄧ殑NoSql鏁版嵁搴扑箣涓锛屼篃琚浜轰滑绉颁负鏁版嵁缁撴瀯链嶅姟鍣ㄣ
2銆 浜掕仈缃戞椂浠h儗鏅涓嫔ぇ链洪亣锛屼粈涔堣佷娇鐢∟osql锛
1锛 褰撴暟鎹閲忕殑镐诲ぇ灏忎竴涓链哄櫒鏀句笉涓嬫椂銆
2锛 鏁版嵁绱㈠紩涓涓链哄櫒镄勫唴瀛樻斁涓崭笅镞躲
3锛 璁块梾閲忥纸璇诲啓娣峰悎锛変竴涓瀹炰緥鏀句笉涓嬫椂銆
鍗曟満镞朵唬妯″瀷
濡傛灉姣忔″瓨鍌ㄦ垚鍗冧笂涓囨浔鏁版嵁锛岃繖镙峰緢浼氩艰嚧MySQL镄勬ц兘寰埚樊锛屽瓨鍌ㄤ互鍙婅诲彇阃熷害寰堟参锛岀劧钖庡氨婕斿彉鎴愮紦瀛+mysql+鍨傜洿𨰾嗗垎镄勬柟寮忋
Cache浣滀负涓闂寸紦瀛
灏嗘墍链夌殑鏁版嵁鍏堜缭瀛桦埌缂揿瓨涓锛岀劧钖庡啀瀛桦叆mysql涓锛屽噺灏忔暟鎹搴揿帇锷涳纴鎻愰珮鏁堢巼銆 浣嗘槸褰撴暟鎹鍐嶆″炲姞鍒板张涓涓閲忕骇锛屼笂闱㈢殑鏂瑰纺涔熶笉鑳芥弧瓒抽渶姹傦纴鐢变簬鏁版嵁搴撶殑鍐椤叆铡嫔姏澧炲姞锛孧emcached鍙鑳界紦瑙f暟鎹搴撶殑璇诲彇铡嫔姏銆
璇诲啓闆嗕腑鍦ㄤ竴涓鏁版嵁搴扑笂璁╂暟鎹搴扑笉鍫閲嶈礋锛屽ぇ閮ㄥ垎缃戠珯寮濮嬩娇鐢ㄤ富浠庡嶅埗鎶链𨱒ヨ揪鍒拌诲啓鍒嗙伙纴浠ユ彁楂樿诲啓镐ц兘鍜岃诲簱镄勫彲镓╁𪾢镐с侻ysql镄刴aster-slave妯″纺鎴愪负杩欎釜镞跺欑殑缃戠珯镙囬厤浜嗐
涓讳粠鍒嗙绘ā寮
鍦╮edis镄勯珮阃熺紦瀛桡纴MySQL镄勪富浠庡嶅埗锛岃诲啓鍒嗙荤殑锘虹涔嬩笂锛岃繖镞祃ySQL涓诲簱镄勫啓铡嫔姏寮濮嫔嚭鐜扮摱棰堬纴钥屾暟鎹閲忕殑鎸佺画鐚涘烇纴鐢变簬MyISAM浣跨敤琛ㄩ挛锛屽湪楂桦苟鍙戜笅浼氩嚭鐜颁弗閲岖殑阌侀梾棰桡纴澶ч噺镄勯珮骞跺彂MySQL搴旂敤寮濮嬩娇鐢↖nnoDB寮曟搸浠f浛MyISAM銆
鍒呜〃鍒嗗簱妯″纺
灏嗗彉鍖栧皬镄勚佷笟锷$浉鍏崇殑鏀惧湪涓涓鏁版嵁搴掳纴鍙桦寲澶氱殑锛屼笉鐩稿叧镄勬暟鎹鏀惧湪涓涓鏁版嵁搴撱
3銆 nosql鏁版嵁搴撶殑浼桦娍
1锛夋槗镓╁𪾢
杩欎簺绫诲瀷镄勬暟鎹瀛桦偍涓嶉渶瑕佸浐瀹氱殑妯″纺锛屾棤闇澶氢綑镄勬搷浣滃氨鍙浠ヨ繘琛屾í钖戠殑镓╁𪾢銆傜浉瀵逛簬鍏崇郴鍨嬫暟鎹搴揿彲浠ュ噺灏戣〃鍜屽瓧娈电壒鍒澶氱殑𨱍呭喌銆备篃镞犲瀷涔嬮棿鍦ㄦ灦鏋勭殑灞傞溃涓婂甫𨱒ヤ简鍙镓╁𪾢镄勮兘锷
2锛夊ぇ鏁版嵁閲忔彁楂樻ц兘
3锛夊氭牱𨱔垫椿镄勬暟鎹妯″瀷
鍦╪osql涓涓崭粎鍙浠ュ瓨鍌⊿tring锛宧ash锛宻et銆乑set绛夋暟鎹绫诲瀷锛岃缮鍙浠ヤ缭瀛榡avaBean浠ュ强澶氱嶅嶆潅镄勬暟鎹绫诲瀷銆
4銆 NoSql镄勫簲鐢
1锛 澶ф暟鎹镞朵唬娣桦疂銆佸井淇°佷互鍙婂井鍗氱瓑閮藉箍娉涚殑浣跨敤浜唕edis鏁版嵁搴掳纴灏嗕竴浜涘浐瀹氢笉鍙樼殑鏁版嵁渚嫔傚︽牎锛屽尯锘熺瓑锲哄畾镄勪俊鎭淇濆瓨鍦ㄥ叧绯诲瀷鏁版嵁搴扑腑銆傜劧钖庡逛簬缁忓父鍙桦寲镄勬暟鎹渚嫔傛窐瀹濇疮涓鑺傛棩閮戒细链夋瘆杈幂儹闂ㄧ殑鎼灭储鏄剧ず鍦ㄦ悳绱㈡嗭纴褰撹妭镞ヨ繃铡诲叧阌瀛楄嚜锷ㄥ垹闄わ纴涓轰简渚夸簬绠$悊锛屽彲浠ュ皢杩欎簺鏁版嵁淇濆瓨鍦╮edis鏁版嵁搴扑腑锛屽苟璁剧疆杩囨湡镞堕棿锛屽埌杈炬椂闂村氨镊锷ㄥ垹闄ゃ
2锛変负浜嗙紦瑙f暟鎹搴揿帇锷涳纴寰鍗氶栧厛灏嗗彂阃佺殑寰鍗氢缭瀛桦埌redis鏁版嵁搴掳纴镊宸卞彲浠ョ珛鍗虫煡鐪嫔埌锛岀劧钖庡皢鍐呭瓨涓镄勬暟鎹钖屾ュ埌鍏崇郴鍨嬫暟鎹搴撱
浠ヤ笂鍐呭逛粎渚涘弬钥冿紒
鎺ㄨ崘瑙嗛戞暀绋嬶细redis鏁欑▼
B. thinkphp5模型如何使用redis操作数据库CURD操作
模型中添加如下代码,可实现更新或插入前删除缓存:
protected static function init()
{
TurnGiftSetting::beforeInsert(function ($model) {
$redis = new Redis(config('redis'));
$redis->rm(self::$redisKey);
});
TurnGiftSetting::beforeUpdate(function ($model) {
$redis = new Redis(config('redis'));
$redis->rm(self::$redisKey);
});
TurnGiftSetting::beforeDelete(function ($model) {
$redis = new Redis(config('redis'));
$redis->rm(self::$redisKey);
});
TurnGiftSetting::beforeWrite(function ($model) {
$redis = new Redis(config('redis'));
$redis->rm(self::$redisKey);
});
}
C. thinkphp redis 怎么选择数据库
1、redis 中的每一个数据库,都由一个 redisDb 的结构存储。其中,redisDb.id 存储着 redis 数据库以整数表示的号码。redisDb.dict 存储着该库所有的键值对数据。redisDb.expires 保存着每一个键的过期时间。
2、当redis 服务器初始化时,会预先分配 16 个数据库(该数量可以通过配置文件配置),所有数据库保存到结构 redisServer 的一个成员 redisServer.db 数组中。当我们选择数据库 select number 时,程序直接通过 redisServer.db[number] 来切换数据库。有时候当程序需要知道自己是在哪个数据库时,直接读取 redisDb.id 即可。
3、既然我们知道一个数据库的所有键值都存储在redisDb.dict中,那么我们要知道如果找到key的位置,就有必要了解一下dict 的结构了:
typedef struct dict {
// 特定于类型的处理函数
dictType *type;
// 类型处理函数的私有数据
void *privdata;
// 哈希表(2个)
dictht ht[2];
// 记录 rehash 进度的标志,值为-1 表示 rehash 未进行
int rehashidx;
// 当前正在运作的安全迭代器数量
int iterators;
} dict;
由上述的结构可以看出,redis 的字典使用哈希表作为其底层实现。dict 类型使用的两个指向哈希表的指针,其中 0 号哈希表(ht[0])主要用于存储数据库的所有键值,而1号哈希表主要用于程序对 0 号哈希表进行 rehash 时使用,rehash 一般是在添加新值时会触发,这里不做过多的赘述。所以redis 中查找一个key,其实就是对进行该dict 结构中的 ht[0] 进行查找操作。
4、既然是哈希,那么我们知道就会有哈希碰撞,那么当多个键哈希之后为同一个值怎么办呢?redis采取链表的方式来存储多个哈希碰撞的键。也就是说,当根据key的哈希值找到该列表后,如果列表的长度大于1,那么我们需要遍历该链表来找到我们所查找的key。当然,一般情况下链表长度都为是1,所以时间复杂度可看作o(1)。
二、当redis 拿到一个key 时,如果找到该key的位置。
了解了上述知识之后,我们就可以来分析redis如果在内存找到一个key了。
1、当拿到一个key后, redis 先判断当前库的0号哈希表是否为空,即:if (dict->ht[0].size == 0)。如果为true直接返回NULL。
2、判断该0号哈希表是否需要rehash,因为如果在进行rehash,那么两个表中者有可能存储该key。如果正在进行rehash,将调用一次_dictRehashStep方法,_dictRehashStep 用于对数据库字典、以及哈希键的字典进行被动 rehash,这里不作赘述。
3、计算哈希表,根据当前字典与key进行哈希值的计算。
4、根据哈希值与当前字典计算哈希表的索引值。
5、根据索引值在哈希表中取出链表,遍历该链表找到key的位置。一般情况,该链表长度为1。
6、当 ht[0] 查找完了之后,再进行了次rehash判断,如果未在rehashing,则直接结束,否则对ht[1]重复345步骤。
到此我们就找到了key在内存中的位置了。
D. php 怎么把session写入redis
一、 安装phpredis扩展
php连接redis需要安装phpredis扩展。
下载地址:https://github.com/phpredis/phpredis/releases,选用相应版本。
笔者用的是php5.6.29,下载了phpredis-3.0.0安装出了问题,于是换成phpredis-2.2.8,正常。
# tar zxvf phpredis-2.2.8.tar.gz && cd phpredis-2.2.8
# /usr/local/php/bin/phpize
# ./configure --with-php-config=/usr/local/php/bin/php-config
# make && make install
正常情况下会提示在下面目录下生成redis.so文件
Installing shared extensions: /usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/
# sed -i '$aextension="redis.so"' /usr/local/php/etc/php.ini
# service httpd restart
验证:
# php -i | grep redis | grep -xq redis && echo ok || echo "phpredis extended failure"
二、 配置php连接redis(笔者使用的阿里云redis)
# sed -i 's/session.save_handler.*/session.save_handler = redis/' /usr/local/php/etc/php.ini
# sed -i '/session.save_handler/asession.save_path ="tcp://xxxx.rds.aliyuncs.com?auth=redis密码"' /usr/local/php/etc/php.ini
# service httpd restart
E. windows php使用redis怎么后台运行
1. 进入 DOS窗口
2. 在进入Redis的安装目录
3. 输入:redis-server --service-install redis.windows.conf --loglevel verbose ( 安装redis服务 )
4. 输入:redis-server --service-start ( 启动服务 )
5. 输入:redis-server --service-stop
主要是需要安装redis的服务,电脑重新启动之后如果redis的服务没启动需要手动启动
F. phpredis igbinary的编译安装有关问题怎么解决
1,下载安装包
来到php的官方扩展库下载地址,然后搜索redis,找到stable版本的稳定包,下载到本地,准备扩展安装
pecl(The PHP Extension Community Library)地址:http://www.pecl.php.net/
redis下载地址:http://www.pecl.php.net/get/redis-2.2.4.tgz
2,编译扩展包
首先解压redis扩展包,然后进入到目录中,执行:
/usr/local/php/bin/phpize
如果没报错,继续执行:
./configure --with-php-config=/usr/local/php/bin/php-config
如果没有报configure error 则继续执行:
make && make install
成功之后会提示redis.so文件的存放目录,如下:
然后将redis.so文件拷贝到php的扩展目录下,然后修改php.ini文件增加一行:
extension=redis.so
重启服务器,就可以在phpinfo()中看到扩展信息了!
G. PHP Redis是使用connect还是pconnect
首先先介绍下connect和pconnect的区别。
connect:脚本结束之后连接就释放了。
pconnect:脚本结束之后连接不释放,连接保持在php-fpm进程中。
所以使用pconnect代替connect,可以减少频繁建立redis连接的消耗。
H. thinkphp5.0镐庝箞浣跨敤redis
璋幂敤鏂规硶
$redis = new Redis();
$redis->set('username','zongs');echo $redis->get('username');
I. php redis做mysql的缓存,怎么异步redis同步到mysql数据库
对于变化频率非常快的数据来说,如果还选择传统的静态缓存方式(Memocached、File System等)展示数据,可能在缓存的存取上会有很大的开销,并不能很好的满足需要,而Redis这样基于内存的NoSQL数据库,就非常适合担任实时数据的容器。
但是往往又有数据可靠性的需求,采用MySQL作为数据存储,不会因为内存问题而引起数据丢失,同时也可以利用关系数据库的特性实现很多功能。
所以就会很自然的想到是否可以采用MySQL作为数据存储引擎,Redis则作为Cache。而这种需求目前还没有看到有特别成熟的解决方案或工具,因此采用Gearman+PHP+MySQL UDF的组合异步实现MySQL到Redis的数据复制。
MySQL到Redis数据复制方案
无论MySQL还是Redis,自身都带有数据同步的机制,比较常用的MySQL的Master/Slave模式,就是由Slave端分析Master的binlog来实现的,这样的数据复制其实还是一个异步过程,只不过当服务器都在同一内网时,异步的延迟几乎可以忽略。
那么理论上也可以用同样方式,分析MySQL的binlog文件并将数据插入Redis。但是这需要对binlog文件以及MySQL有非常深入的理解,同时由于binlog存在Statement/Row/Mixedlevel多种形式,分析binlog实现同步的工作量是非常大的。
因此这里选择了一种开发成本更加低廉的方式,借用已经比较成熟的MySQL UDF,将MySQL数据首先放入Gearman中,然后通过一个自己编写的PHP Gearman Worker,将数据同步到Redis。比分析binlog的方式增加了不少流程,但是实现成本更低,更容易操作。
Gearman的安装与使用
Gearman是一个支持分布式的任务分发框架。设计简洁,获得了非常广泛的支持。一个典型的Gearman应用包括以下这些部分:
Gearman Job Server:Gearman核心程序,需要编译安装并以守护进程形式运行在后台
Gearman Client:可以理解为任务的收件员,比如在后台执行一个发送邮件的任务,可以在程序中调用一个Gearman Client并传入邮件的信息,然后就可以将执行结果立即展示给用户,而任务本身会慢慢在后台运行。
Gearman Worker:任务的真正执行者,一般需要自己编写具体逻辑并通过守护进程方式运行,Gearman Worker接收到Gearman Client传递的任务内容后,会按顺序处理。
以前曾经介绍过类似的后台任务处理项目Resque。两者的设计其实非常接近,简单可以类比为:
Gearman Job Server:对应Resque的Redis部分
Gearman Client:对应Resque的Queue操作
Gearman Worker:对应Resque的Worker和Job
这里之所以选择Gearman而不是Resque是因为Gearman提供了比较好用的MySQL UDF,工作量更小。
安装Gearman及PHP Gearman扩展
以下均以Ubuntu12.04为例。
apt-get install gearman gearman-server libgearman-dev
检查Gearman的运行状况:
/etc/init.d/gearman-job-server status
* gearmand is running
说明Gearman已经安装成功。
PHP的Gearman扩展可以通过pecl直接安装
pecl install gearman
echo "extension=gearman.so">/etc/php5/conf.d/gearman.ini
service php5-fpm restart
但是实测发现ubuntu默认安装的gearman版本过低,直接运行pecl install gearman会报错
configure: error: libgearman version 1.1.0or later required
因此Gearman + PHP扩展建议通过编译方式安装,这里为了简单说明,选择安装旧版本扩展:
pecl install gearman-1.0.3
Gearman + PHP实例
为了更容易理解后文Gearman的运行流程,这里不妨从一个最简单的Gearman实例来说明,比如要进行一个文件处理的操作,首先编写一个Gearman Client并命名为client.php:
<?php
$client =newGearmanClient();
$client->addServer();
$client->doBackground('writeLog','Log content');
echo '文件已经在后台操作';
运行这个文件,相当于模拟用户请求一个Web页面后,将处理结束的信息返回用户:
php client.php
查看一下Gearman的状况:
(echo status ; sleep 0.1)| netcat127.0.0.14730
可以看到输出为
writeLog 100.
说明已经在Gearman中建立了一个名为writeLog的任务,并且有1个任务在队列等待中。
而上面的4列分别代表当前的Gearman的运行状态:
任务名称
在等待队列中的任务
正在运行的任务
正在运行的Worker进程
可以使用watch进行实时监控:
watch -n 1"(echo status; sleep 0.1) | nc 127.0.0.1 4730"
然后我们需要编写一个Gearman Worker命名为worker.php:
<?php
$worker =newGearmanWorker();
$worker->addServer();
$worker->addFunction('writeLog','writeLog');while($worker->work());function writeLog($job){
$log = $job->workload();file_put_contents(__DIR__ .'/gearman.log', $log ." ", FILE_APPEND | LOCK_EX);}
Worker使用一个while死循环实现守护进程,运行
php worker.php
可以看到Gearman状态变为:
writeLog 001
同时查看同目录下gearman.log,内容应为从Client传入的值Log content。
通过MySQL UDF + Trigger同步数据到Gearman
MySQL要实现与外部程序互通的最好方式还是通过MySQL UDF(MySQL user defined functions)来实现。为了让MySQL能将数据传入Gearman,这里使用了lib_mysqludf_json和gearman-mysql-udf的组合。
安装lib_mysqludf_json
使用lib_mysqludf_json的原因是因为Gearman只接受字符串作为入口参数,可以通过lib_mysqludf_json将MySQL中的数据编码为JSON字符串
apt-get install libmysqlclient-dev
wget https://github.com/mysqludf/lib_mysqludf_json/archive/master.zip
unzip master.zip
cd lib_mysqludf_json-master/
rm lib_mysqludf_json.so
gcc $(mysql_config --cflags)-shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c
可以看到重新编译生成了 lib_mysqludf_json.so 文件,此时需要查看MySQL的插件安装路径:
mysql -u root -pPASSWORD --execute="show variables like '%plugin%';"+---------------+------------------------+|Variable_name|Value|+---------------+------------------------+| plugin_dir |/usr/lib/mysql/plugin/|+---------------+------------------------+
然后将 lib_mysqludf_json.so 文件复制到对应位置:
cp lib_mysqludf_json.so /usr/lib/mysql/plugin/
最后登入MySQL运行语句注册UDF函数:
CREATE FUNCTION json_object RETURNS STRING SONAME 'lib_mysqludf_json.so';
安装gearman-mysql-udf
方法几乎一样:
apt-get install libgearman-dev
wget https://launchpad.net/gearman-mysql-udf/trunk/0.6/+download/gearman-mysql-udf-0.6.tar.gz
tar -xzf gearman-mysql-udf-0.6.tar.gz
cd gearman-mysql-udf-0.6./configure --with-mysql=/usr/bin/mysql_config
-libdir=/usr/lib/mysql/plugin/
make && make install
登入MySQL运行语句注册UDF函数:
CREATE FUNCTION gman_do_background RETURNS STRING SONAME 'libgearman_mysql_udf.so';
CREATE FUNCTION gman_servers_set RETURNS STRING SONAME 'libgearman_mysql_udf.so';
最后指定Gearman服务器的信息:
SELECT gman_servers_set('127.0.0.1:4730');
通过MySQL触发器实现数据同步
最终同步哪些数据,同步的条件,还是需要根据实际情况决定,比如将数据表data的数据在每次更新时同步,那么编写Trigger如下:
DELIMITER $$
CREATE TRIGGER datatoredis AFTER UPDATE ON data
FOR EACH ROW BEGIN
SET @ret=gman_do_background('syncToRedis', json_object(NEW.id as`id`, NEW.volume as`volume`));END$$
DELIMITER ;
尝试在数据库中更新一条数据查看Gearman是否生效。
Gearman PHP Worker将MySQL数据异步复制到Redis
Redis作为时下当热的NoSQL缓存解决方案无需过多介绍,其安装及使用也非常简单:
apt-get install redis-server
pecl install redis
echo "extension=redis.so">/etc/php5/conf.d/redis.ini
然后编写一个Gearman Worker:redis_worker.php
#!/usr/bin/env php<?
$worker =newGearmanWorker();
$worker->addServer();
$worker->addFunction('syncToRedis','syncToRedis');
$redis =newRedis();
$redis->connect('127.0.0.1',6379);while($worker->work());function syncToRedis($job){global $redis;
$workString = $job->workload();
$work = json_decode($workString);if(!isset($work->id)){returnfalse;}
$redis->set($work->id, $workString);}
最后需要将Worker在后台运行:
nohup php redis_worker.php &
通过这种方式将MySQL数据复制到Redis,经测试单Worker基本可以瞬时完成。
J. php 使用redis锁限制并发访问类示例
本文介绍了php
使用redis锁限制并发访问类,并详细的介绍了并发访问限制方法。
1.并发访问限制问题
对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功。
例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制的情况下,用户则可以使用同一个换领码同时兑换到多张优惠券。
伪代码如下:
if
A(可以换领)
B(执行换领)
C(更新为已换领)
D(结束)
如果用户并发提交换领码,都能通过可以换领(A)的判断,因为必须有一个执行换领(B)后,才会更新为已换领(C)。因此如果用户在有一个更新为已换领之前,有多少次请求,这些请求都可以执行成功。
2.并发访问限制方法
使用文件锁可以实现并发访问限制,但对于分布式架构的环境,使用文件锁不能保证多台服务器的并发访问限制。
Redis是一个开源的使用ANSI
C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
本文将使用其setnx方法实现分布式锁功能。setnx即Set
it
N**ot
eX**ists。
当键值不存在时,插入成功(获取锁成功),如果键值已经存在,则插入失败(获取锁失败)
RedisLock.class.PHP
<?php
/**
*
Redis锁操作类
*
Date:
2016-06-30
*
Author:
fdipzone
*
Ver:
1.0
*
*
Func:
*
public
lock
获取锁
*
public
unlock
释放锁
*
private
connect
连接
*/
class
RedisLock
{
//
class
start
private
$_config;
private
$_redis;
/**
*
初始化
*
@param
Array
$config
redis连接设定
*/
public
function
__construct($config=array()){
$this->_config
=
$config;
$this->_redis
=
$this->connect();
}
/**
*
获取锁
*
@param
String
$key
锁标识
*
@param
Int
$expire
锁过期时间
*
@return
Boolean
*/
public
function
lock($key,
$expire=5){
$is_lock
=
$this->_redis->setnx($key,
time()+$expire);
//
不能获取锁
if(!$is_lock){
//
判断锁是否过期
$lock_time
=
$this->_redis->get($key);
//
锁已过期,删除锁,重新获取
if(time()>$lock_time){
$this->unlock($key);
$is_lock
=
$this->_redis->setnx($key,
time()+$expire);
}
}
return
$is_lock?
true
:
false;
}
/**
*
释放锁
*
@param
String
$key
锁标识
*
@return
Boolean
*/
public
function
unlock($key){
return
$this->_redis->del($key);
}
/**
*
创建redis连接
*
@return
Link
*/
private
function
connect(){
try{
$redis
=
new
Redis();
$redis->connect($this->_config['host'],$this->_config['port'],$this->_config['timeout'],$this->_config['reserved'],$this->_config['retry_interval']);
if(empty($this->_config['auth'])){
$redis->auth($this->_config['auth']);
}
$redis->select($this->_config['index']);
}catch(RedisException
$e){
throw
new
Exception($e->getMessage());
return
false;
}
return
$redis;
}
}
//
class
end
?>
demo.php
<?php
require
'RedisLock.class.php';
$config
=
array(
'host'
=>
'localhost',
'port'
=>
6379,
'index'
=>
0,
'auth'
=>
'',
'timeout'
=>
1,
'reserved'
=>
NULL,
'retry_interval'
=>
100,
);
//
创建redislock对象
$oRedisLock
=
new
RedisLock($config);
//
定义锁标识
$key
=
'mylock';
//
获取锁
$is_lock
=
$oRedisLock->lock($key,
10);
if($is_lock){
echo
'get
lock
success<br>';
echo
'do
sth..<br>';
sleep(5);
echo
'success<br>';
$oRedisLock->unlock($key);
//
获取锁失败
}else{
echo
'request
too
frequently<br>';
}
?>
测试方法:
打开两个不同的浏览器,同时在A,B中访问demo.php
如果先访问的会获取到锁
输出
get
lock
success
do
sth..
success
另一个获取锁失败则会输出request
too
frequently
保证同一时间只有一个访问有效,有效限制并发访问。
为了避免系统突然出错导致死锁,所以在获取锁的时候增加一个过期时间,如果已超过过期时间,即使是锁定状态都会释放锁,避免死锁导致的问题。
源码下载地址:点击查看