1. php每天抓取數據並更新新
以前我用過querylist插件抓數據,伺服器寫和定時器,每天固定時間去運行腳本。朝這個方式試試
高並發下數據的更新,應該 update table xxx set num = num - 1 的方式,這種方式可以保證數據的正確性。
但是會出現 num 為負數的問題,如果庫存為負數,顯然是不合理的。
於是,需要將 num 欄位設置為 無符號整型,這樣就不會出現負數了,因為,如果減到負數,就會更新失敗。
但是這種依然會造成很多無用的更新語句的執行,是不合理的。
於是,update table xxx set num = num - 1 where num > 0,
這樣當 num 等於0之後就不會去更新資料庫了,減少了很多無用的開銷。
這種方式被稱作「樂觀鎖」
此外,對於搶紅包這種非整數的操作,我們應該轉換為整數的操作。
關於搶購超賣的控制
一般搶購功能是一個相對於正常售賣系統來說獨立的子系統,這樣既可以防止搶購時的高並發影響到正常系統,
也可以做到針對於搶購業務的特殊處理。
在後台設計一些功能,可以就昂正常的商品加入到搶購活動中並編輯成為搶購商品,寫入到搶購商品表,當然
也可以把搶購商品表寫入redis而不是數據表。並且在原商品表寫入一個同樣的商品(id相同,用於訂單查看,
此商品不可購買)
如果是數據表,為了控制超賣,需要對表進行行鎖,更新的時候帶上 where goods_amount > 0。
如果是redis,使用 hincrby 一個負數來減庫存,並且 hincrby 會返回改變後的值,再來判斷返回值是否大於0,
因為redis每個命令都是原子性的,這樣不用鎖表就可控制超賣。
2. php redis如何使用
開始在 PHP 中使用 Redis 前,要確保已經安裝了 redis 服務及 PHP redis 驅動,且你的機器上能正常使用 PHP。
PHP安裝redis擴展
/usr/local/php/bin/phpize #php安裝後的路徑
./configure --with-php-config=/usr/local/php/bin/php-config
make && make install
修改php.ini文件
vi /usr/local/php/lib/php.ini
增加如下內容:
extension_dir = "/usr/local/php/lib/php/extensions/no-debug-zts-20090626"
extension=redis.so
安裝完成後重啟php-fpm 或 apache。查看phpinfo信息,就能看到redis擴展。
連接到 redis 服務
<?php
//連接本地的 Redis 服務
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
echo "Connection to server sucessfully";
//查看服務是否運行
echo "Server is running: " . $redis->ping();
?>
執行腳本,輸出結果為:
Connection to server sucessfully
Server is running: PONG
Redis PHP String(字元串) 實例
<?php
//連接本地的 Redis 服務
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
echo "Connection to server sucessfully";
//設置 redis 字元串數據
$redis->set("tutorial-name", "Redis tutorial");
// 獲取存儲的數據並輸出
echo "Stored string in redis:: " . jedis.get("tutorial-name");
?>
執行腳本,輸出結果為:
Connection to server sucessfully
Stored string in redis:: Redis tutorial
Redis PHP List(列表) 實例
<?php
//連接本地的 Redis 服務
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
echo "Connection to server sucessfully";
//存儲數據到列表中
$redis->lpush("tutorial-list", "Redis");
$redis->lpush("tutorial-list", "Mongodb");
$redis->lpush("tutorial-list", "Mysql");
// 獲取存儲的數據並輸出
$arList = $redis->lrange("tutorial-list", 0 ,5);
echo "Stored string in redis:: "
print_r($arList);
?>
執行腳本,輸出結果為:
Connection to server sucessfully
Stored string in redis::
Redis
Mongodb
Mysql
Redis PHP Keys 實例
<?php
//連接本地的 Redis 服務
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
echo "Connection to server sucessfully";
// 獲取數據並輸出
$arList = $redis->keys("*");
echo "Stored keys in redis:: "
print_r($arList);
?>
執行腳本,輸出結果為:
Connection to server sucessfully
Stored string in redis::
tutorial-name
tutorial-list
3. 如何是php支持redis 是如何配置的 是不是還需要擴展什麼的
這個需要在php裡面載入redis模塊,該模塊php默認是沒有的,需要根據自己php的版本下載相應的redis dll 文件,同時還需要一個redis server 伺服器端,只有開啟了redis伺服器和在php.ini中載入了redis dll 模塊,才能正常的使用redis擴展,詳細的配置的話,我就不太懂了,我只是用過,具體的詳細配置沒弄過...
4. 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
保證同一時間只有一個訪問有效,有效限制並發訪問。
為了避免系統突然出錯導致死鎖,所以在獲取鎖的時候增加一個過期時間,如果已超過過期時間,即使是鎖定狀態都會釋放鎖,避免死鎖導致的問題。
源碼下載地址:點擊查看
5. redis為什麼需要watch
不知道你說的watch是啥意思,php操作redis很簡單的,舉個例子:
<?php
//連接本地的 Redis 服務
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
echo "Connection to server sucessfully";
//設置 redis 字元串數據
$redis->set("tutorial-name", "Redis tutorial");
// 獲取存儲的數據並輸出
echo "Stored string in redis:: " . $redis->get("tutorial-name");
?>
這是操作字元串的,還有操作其他redis數據類型的。
要成功先確保你php有裝redis擴展,並且本地redis服務正常跑著的。
如果沒有解決你的問題的話,麻煩你解釋一下問題吧,你看都沒人回你,因為你問題不清楚啊。
如果解決了,就請採納吧。
6. Redis分布式鎖的原理是什麼如何續期
在傳統單體應用單機部署的情況下,並發問題可以通過使用Java並發相關的鎖如synchronized,但是當規模上升到分布式集群的情況下,要控制共享資源訪問,就需要通過分布式鎖來實現。常見的分布式鎖方案如資料庫樂觀鎖,Redis鎖,zk鎖等。
Redis分布式鎖的原理
Redis分布式鎖可以有多種方式實現但是其核心就是通過以下三個Redis命令組合實現。
SETNX SETNX key val 當且僅當key不存在時,set一個key為val的字元串,返回1;若key存在,則什麼都不做,返回0。
Expire expire key timeout 為key設置一個超時時間,單位為second,超過這個時間鎖會自動釋放,避免死鎖。
Delete delete key 刪除key
核心思想
使用setnx獲取鎖。如果成功取到鎖,則使用expire命令為鎖添加一個超時時間,超過該時間則自動釋放鎖。
獲取鎖的時候還設置一個獲取的超時時間,若超過這個時間則放棄獲取鎖。
注意
上面為Redis的一個最簡單的鎖實現原理,實際中還需要考慮更多具體的情況作出相應的調整。如
上面的demo中,當集群系統時間不一致時會有問題
當伺服器異常關閉或是重啟,加鎖後沒來得急設置鎖超時時間,如何避免死鎖
實際開發環境中不確定的因素有很多,需要慢慢地去調整實踐達到理想狀態,可以考慮使用redisson框架來實現。
如何續期?
這個情況比較獨特,出現這個問題的根本原因在於鎖失效的時間小於業務處理的時間導致業務還沒處理完畢鎖就釋放了。那麼解決方案是合理地結合業務去設置鎖失效的時間。
但是也有更好的方案就如前文提到的redisson,其中的可重入鎖概念。
默認情況下,加鎖的時間是30秒.如果加鎖的業務沒有執行完,那麼到 30-10 = 20秒的時候,就會進行一次續期,把鎖重置成30秒。
以上就是redis鎖的原理及續期的方式,希望我的回答能對你有所幫助。
7. 為何Redis用樂觀鎖,而MySQL資料庫卻沒有
簡單來說,Redis使用樂觀鎖,相對於悲觀鎖,在實現中更加簡單,在某些場景中的性能也更好。Redis作為一個輕量級的、快速的緩存引擎,而不是一個全功能的關系型資料庫,既沒有使用悲觀鎖的必要,也難以承受使用悲觀鎖的成本。
詳細來說,要深入到Redis和MySQL的事務處理機制。Redis關於事務的文檔見此:
Transactions(事務)
Redis對於事務只提供了非常有限的支持,其實更多地是試圖繞過問題。
首先,Redis對於同一事務中的一組操作,而不是立即執行,而是放入一個queue中,當執行到EXEC時,再一起執行。事務執行是全局獨占的,也就是同一時間只有一個事務被執行,中途不能被其它事務打斷。Redis用這種最簡單的、也是性能最差的方式避免了race condition。
其次,在Redis的事務中,如果有一個或多個操作失敗,其它操作仍然會成功,也就是說它根本沒有回滾機制。
這種方式會帶來很多嚴重的問題,其中之一是,無法先讀取某個數值後再進行依賴這個值的操作,因為放在一個事務里會被在同一個瞬間執行,不放在同一個事務里又會導致race condition。解決方法是使用WATCH,它會監視一個或多個變數,如果變數的值在調用WATCH以後和事務提交之前被別的事務修改過了,整個事務都會失敗。這類似於操作系統中的CAS(Compare and Set)。我不知道WATCH具體是怎麼實現的,但是我推測它監控了指定變數的版本號。
即使有了WATCH,Redis的事務也是受到嚴重限制的。第一,它沒有實現讀數據時的一致性,因為WATCH對於讀操作不起作用。第二,它不支持回滾。第三,在對同一變數存在大量並發寫操作時,性能會非常差,因為每次提交事務時,WATCH監控的變數都已經被修改了,導致事務將多次提交失敗。但是,Redis本來就是一個KV類型的緩存引擎,要處理的是大量讀少量寫的場景,對一致性也沒有要求。
MySQL就完全不一樣了,作為一個典型的關系型資料庫,它需要完整地實現ACID,所以Redis的方式是解決不了它的問題的。
MySQL中的MVCC機制(Oracle的也是),通過undo 日誌來獲取某個行記錄的歷史快照,從而實現了所謂的讀一致性。它的目的是讀取某個時間點上的歷史數據(而不是可能已經被修改了的數據),而不是避免悲觀鎖的使用。嚴格地說這不能稱之為樂觀鎖,因為它既不Compare當前版本和歷史版本,也不進行Set。事實上,在讀取記錄的歷史快照時,當前記錄有可能(由於並發的寫操作)已經被加上獨占鎖。
進一步的問題是:有沒有可能使用樂觀鎖來實現RDBMS中的寫一致性?有沒有可能使用樂觀鎖實現完整的ACID特性?
回答是可以。例如,MS SQL SERVER的Hekaton引擎通過一套基於時間戳的多版本管理系統,實現了不使用了悲觀鎖的ACID。
但是,這並不意味著樂觀鎖必然優於悲觀鎖。除了維護多版本的開銷以外,樂觀鎖無法避免的一個問題是,當多個寫操作試圖更新同一個對象時,只有第一個操作可以成功,其它的操作都會在Compare時失敗然後回滾,從而造成極大的性能問題。在這種情況下,樂觀鎖的性能會低於悲觀鎖。
目前的趨勢似乎是,大規模的分布式資料庫更傾向於使用樂觀鎖來達到所謂的external consistency,因為基於傳統悲觀鎖的分布式鎖在集群大到一定程度以後(從幾百台擴展到成千上萬台時),性能開銷就大得無法接受了。Google的Spanner就是基於樂觀鎖。當然這完全是另外一個問題了。
8. 如何正確使用redis隊列處理php秒殺並發問題
1. redis中保存的是數組(序列化),絕對不要保存SQL,保存SQL的方法很蛋疼 保存數組是為了資料庫安全(萬一sql語句有錯誤,任務就直接失敗了),靈活度和兼容性
2.伺服器後台作一個shell腳本,死循環,不斷從隊列中取數據,進行處理.如次反復,如果沒有數據,也立即嘗試取數據---不要擔心性能問題,後台單並發請求,不會造成性能問題
3.因為隊列中保存的是數組,不存在這個問題