php使用redis的幾種常見方式和用法(轉)
一、簡單的字串快取
比如針對一些sql查詢較慢,更新不頻繁的資料進行快取。
<?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379, 60); $sql = 'select * from tb_order order by id desc limit 10'; //虛擬碼,從資料庫中獲取資料 $data = $db->query($sql); $data = json_encode($data, JSON_UNESCAPED_UNICODE); $key = md5($sql); //快取資料 $redis->set($key, $value, 60); //獲取資料 $data = $redis->get($key); print_r(json_decode($data, true));
二、通過列表模擬簡單佇列
比如我們需要批量的傳送郵件,可以把傳送郵件的任務存入佇列中,然後啟多個php指令碼從佇列中讀取任務去傳送郵件。
也可以用來處理商品秒殺,使用者點選搶購時,把一個個的使用者搶購任務放入佇列中,序列化處理,判斷佇列數量,防止超賣的發生。
<?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379, 60); //迴圈的把傳送1000條郵件任務插入佇列for ($ix = 0; $ix < 1000; $ix++) { $redis->lPush('send_email_queue', json_encode([ 'id' => $ix, 'send' => '[email protected]', 'receive' => '[email protected]', 'title' => 'xxx', 'body' => 'xxx', ])); } sleep(3); //從佇列中取任務,執行任務 while ($count = $redis->lLen('send_email_queue')) { echo "當前任務佇列數 {$count} <br>"; $task = $redis->rpop('send_email_queue'); $task = json_decode($task, true); //虛擬碼,傳送郵件 $mailer->send($task['send'], $task['receive'], $task['title'], $task['body']); echo "任務 {$task['id']} 郵件傳送成功<br>"; }
三、通過watch + multi 來實現樂觀鎖
樂觀鎖,顧名思義,樂觀的認為資料不會被修改,只有當更新時才去判斷資料是否被修改過,通常用版本號或時間戳來實現。
redis中通過watch和multi來實現,watch會監視給定的key是否發生更改,當exec的時候如果監視的key發生過改變,則整個事務會失敗。
當然我們可以呼叫多次watch監視多個key。
<?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379, 60); //設定商品的庫存數為100 $redis->set('goods_stock_nums', 100); //監視該key $redis->watch('goods_stock_nums'); //開啟事務 $redis->multi(); //修改庫存數 $redis->decr('goods_stock_nums'); //提交事務,如果在此期間有其他請求修改了該key,那麼事務會失敗 if ($redis->exec()) { echo '搶購成功'; } else { echo '資料錯誤,請重新再試'; }
四、使用 set 來實現悲觀鎖
悲觀鎖,顧名思義,悲觀的認為資料總是會被修改,所以在操作前都會先加上鎖,操作完後,再釋放鎖。
<?php function getRedis() { $redis = new Redis(); $redis->connect('127.0.0.1', 6379, 60); return $redis; } function lock($key, $random) { $redis = getRedis(); return $redis->set($key, $random, ['nx', 'ex' => 3]); } function unlock($key, $random) { $redis = getRedis(); //使用lua指令碼保證原子性 $script = 'if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end'; return $redis->eval($script, [$key, $random], 1); } function decrGoodsStockNums() { $redis = getRedis(); //獲取商品庫存數 $ret = $redis->get('goods_stock_nums'); if ($ret === false) { return false; } if ($ret <= 0) { return false; } $random = mt_rand(); //先獲取鎖 if (lock('goods_stock_nums_lock', $random)) { //修改庫存數 $redis->decr('goods_stock_nums'); //釋放鎖 unlock('goods_stock_nums_lock', $random); return true; } else { usleep(100); decrGoodsStockNums(); } } decrGoodsStockNums();
五、使用publish +subscribe 完成釋出和訂閱
釋出程式碼:
<?php $redis = new Redis(); $redis->pconnect('127.0.0.1', 6379); $ix = 0; //釋出內容 while (true) { $redis->publish('news', json_encode([ 'title' => '我是新聞標題' . $ix, 'content' => '我是新聞內容' . $ix, 'time' => date('Y-m-d H:i:s'), ])); $ix++; sleep(1); }
訂閱程式碼:
<?php $redis = new Redis(); $redis->pconnect('127.0.0.1', 6379); //訂閱內容 $redis->subscribe(['news'], function ($redis, $channel, $msg) { $msg = json_decode($msg, true); echo "標題: {$msg['title']} 內容: {$msg['content']} 時間: {$msg['time']} <br>"; });
轉自:https://www.cnblogs.com/jkko123/p/10491342.html
一、簡單的字串快取
比如針對一些sql查詢較慢,更新不頻繁的資料進行快取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php
$redis = new Redis();
$redis ->connect( '127.0.0.1' , 6379, 60);
$sql = 'select * from tb_order order by id desc limit 10' ;
//虛擬碼,從資料庫中獲取資料
$data = $db ->query( $sql );
$data = json_encode( $data , JSON_UNESCAPED_UNICODE);
$key = md5( $sql );
//快取資料
$redis ->set( $key , $value , 60);
//獲取資料
$data = $redis ->get( $key );
print_r(json_decode( $data , true));
|
二、通過列表模擬簡單佇列
比如我們需要批量的傳送郵件,可以把傳送郵件的任務存入佇列中,然後啟多個php指令碼從佇列中讀取任務去傳送郵件。
也可以用來處理商品秒殺,使用者點選搶購時,把一個個的使用者搶購任務放入佇列中,序列化處理,判斷佇列數量,防止超賣的發生。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?php
$redis = new Redis();
$redis ->connect( '127.0.0.1' , 6379, 60);
//迴圈的把傳送1000條郵件任務插入佇列
for ( $ix = 0; $ix < 1000; $ix ++) {
$redis ->lPush( 'send_email_queue' , json_encode([
'id' => $ix ,
'send' => '[email protected]' ,
'receive' => '[email protected]' ,
'title' => 'xxx' ,
'body' => 'xxx' ,
]));
}
sleep(3);
//從佇列中取任務,執行任務
while ( $count = $redis ->lLen( 'send_email_queue' )) {
echo "當前任務佇列數 {$count} <br>" ;
$task = $redis ->rpop( 'send_email_queue' );
$task = json_decode( $task , true);
//虛擬碼,傳送郵件
$mailer ->send( $task [ 'send' ], $task [ 'receive' ], $task [ 'title' ], $task [ 'body' ]);
echo "任務 {$task['id']} 郵件傳送成功<br>" ;
}
|
三、通過watch + multi 來實現樂觀鎖
樂觀鎖,顧名思義,樂觀的認為資料不會被修改,只有當更新時才去判斷資料是否被修改過,通常用版本號或時間戳來實現。
redis中通過watch和multi來實現,watch會監視給定的key是否發生更改,當exec的時候如果監視的key發生過改變,則整個事務會失敗。
當然我們可以呼叫多次watch監視多個key。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php
$redis = new Redis();
$redis ->connect( '127.0.0.1' , 6379, 60);
//設定商品的庫存數為100
$redis ->set( 'goods_stock_nums' , 100);
//監視該key
$redis ->watch( 'goods_stock_nums' );
//開啟事務
$redis ->multi();
//修改庫存數
$redis ->decr( 'goods_stock_nums' );
//提交事務,如果在此期間有其他請求修改了該key,那麼事務會失敗
if ( $redis -> exec ()) {
echo '搶購成功' ;
} else {
echo '資料錯誤,請重新再試' ;
}
|
四、使用 set 來實現悲觀鎖
悲觀鎖,顧名思義,悲觀的認為資料總是會被修改,所以在操作前都會先加上鎖,操作完後,再釋放鎖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
<?php
function getRedis()
{
$redis = new Redis();
$redis ->connect( '127.0.0.1' , 6379, 60);
return $redis ;
}
function lock( $key , $random )
{
$redis = getRedis();
return $redis ->set( $key , $random , [ 'nx' , 'ex' => 3]);
}
function unlock( $key , $random )
{
$redis = getRedis();
//使用lua指令碼保證原子性
$script = 'if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end' ;
return $redis -> eval ( $script , [ $key , $random ], 1);
}
function decrGoodsStockNums()
{
$redis = getRedis();
//獲取商品庫存數
$ret = $redis ->get( 'goods_stock_nums' );
if ( $ret === false) {
return false;
}
if ( $ret <= 0) {
return false;
}
$random = mt_rand();
//先獲取鎖
if (lock( 'goods_stock_nums_lock' , $random )) {
//修改庫存數
$redis ->decr( 'goods_stock_nums' );
//釋放鎖
unlock( 'goods_stock_nums_lock' , $random );
return true;
} else {
usleep(100);
decrGoodsStockNums();
}
}
decrGoodsStockNums();
|
五、使用publish +subscribe 完成釋出和訂閱
釋出程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php
$redis = new Redis();
$redis ->pconnect( '127.0.0.1' , 6379);
$ix = 0;
//釋出內容
while (true) {
$redis ->publish( 'news' , json_encode([
'title' => '我是新聞標題' . $ix ,
'content' => '我是新聞內容' . $ix ,
'time' => date ( 'Y-m-d H:i:s' ),
]));
$ix ++;
sleep(1);
}
|
訂閱程式碼:
1 2 3 4 5 6 7 8 9 10 |
<?php
$redis = new Redis();
$redis ->pconnect( '127.0.0.1' , 6379);
//訂閱內容
$redis ->subscribe([ 'news' ], function ( $redis , $channel , $msg ) {
$msg = json_decode( $msg , true);
echo "標題: {$msg['title']} 內容: {$msg['content']} 時間: {$msg['time']} <br>" ;
});
|