目錄
Redis簡介
Redis是一個key-value存儲系統。和Memcached類似,它支持豐富的數據類型,最爲常用的數據類型主要有五種,分別是:string(字符串)、 hash(哈希)、list(鏈表)、set(集合)和zset(有序集合)。Redis通常將數據存儲於內存中,或被配置爲使用虛擬內存。與memcached不同的是,redis會週期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件,以此來達到數據持久化的目的,並且在此基礎上實現了master-slave(主從)同步。當前 Redis的應用已經非常廣泛,國內像新浪、淘寶,國外像 Flickr、Github等均在使用Redis的緩存服務。
環境要求
- Windows 64位操作系統
- php-7.2.2-Win32-VC15-x64
- Redis-x64-4.0.2.2
- php_redis-4.0.2-7.2-ts-vc15-x64
- php_igbinary-2.0.7-7.2-ts-vc15-x64(php_redis擴展依賴)
資源下載地址
php7.2、Redis、php-redis、php-igbinary
注意事項:windows環境下,https://github.com/MicrosoftArchive/redis/releases資源列表裏面能找到的最新版本爲3.2,但是卻沒有資源庫能夠提供對應版本的php-redis擴展,真TM扯淡。
安裝Redis
將下載下來的redis壓縮包Redis-x64-4.0.2.2.zip解壓至某個目錄,如圖:
根據自己所採用的啓動方式,修改對應的配置文件。另外,出於安全方面的考慮,通常需要爲redis設置連接密碼,找到配置文件中含有requirepass foobared字樣的行,在其下面新增一行,如圖:
設置Redis連接密碼後,客戶端在連接Redis時需要通過AUTH <password>命令提供密碼。
數據持久化
Redis提供了兩種持久化的方式,將內存中的數據,存儲在磁盤上。分別是rdb和aof(默認關閉),默認使用的是rdb方式,我們可以根據自己的需求,決定採用哪一種方式。
RDB方式
Redis通過創建快照的方式獲取某一時刻Redis中所有數據的副本,然後將其存放在指定的磁盤文件中。用戶可以針對該快照進行各種操作,比如:將快照文件複製到其他服務器從而完成Redis的主從複製,或者將快照留在原地,服務器重啓的時候重用數據。
rdb配置
# 配置rdb文件名
dbfilename dump.rdb
# 配置rdb文件目錄
dir ./
# 策略配置
# 在規定的時間內,Redis發生寫操作的個數滿足條件,會觸發持久化命令。
# 當用戶設置了多個save的選項配置,只要其中任一條滿足,Redis都會觸發一次持久化操作,比如:900秒之內至少一次寫操作,或者300秒之內至少發生10次寫操作,或者60秒之內至少發生10000次寫操作都會觸發快照命令。
save 900 1
save 300 10
save 60 10000
AOF方式
Redis的AOF持久化策略是將發送到Redis服務器端的每一條命令都記錄下來,並且保存到硬盤的AOF文件中,類似於打開日誌文件,來一條命令就記錄一條。
aof配置
# 是否開啓AOF模式,默認爲no,yes表示開啓
appendonly no
# 配置aof文件名
appendfilename "appendonly.aof"
# 配置aof文件目錄
dir ./
# AOF同步頻率,默認爲 everysec
# always 表示每次有數據發生修改時都會寫入AOF文件,這種方式會嚴重降低redis的性能。
# everysec 表示表示每秒鐘同步一次,該策略爲AOF的缺省策略。
# no 表示讓操作系統來決定應該何時進行同步,Linux系統往往可能30秒纔會執行一次
appendfsync everysec
# yes表示新寫操作不進行同步,只是暫存在緩衝區裏,避免造成磁盤IO操作衝突,等重寫完成後再寫入,默認爲no
no-appendfsync-on-rewrite no
# Redis恢復時,是否忽略最後一條可能存在問題的指令(因爲最後一條指令可能存在問題,比如寫一半時突然斷電了),默認爲yes
aof-load-truncated yes
注意:如果RDB與AOF同時開啓,Redis 默認會先加載AOF的配置文件。
主從複製
在Redis中實現主從複製比較簡單,只需要修改slave服務器配置文件中的slave相關參數即可。
# 設置master主服務器IP和端口
slaveof 127.0.0.1 10002
# slave是否只讀,從服務器負責讀操作,主服務器負責寫操作,從而實現讀寫分離
slave-read-only yes
主從同步原理
1. Slave服務啓動,主動連接Master,併發送SYNC命令,請求初始化同步
2. Master收到SYNC後,執行BGSAVE命令生成RDB文件,並緩存該時間段內的寫命令
3. Master完成RDB文件後,將其發送給所有Slave服務器,
4. Slave服務器接收到RDB文件後,刪除內存中舊的緩存數據,並裝載RDB文件
5. Master在發送完RDB後,即刻向所有Slave服務器發送緩存中的寫命令
6. 至此初始化完成,後續進行增量同步
啓動Redis
此處有兩種啓動方式,通過命令行和通過系統服務的方式。
命令行方式啓動:
切換到redis的根目錄,比如根目錄爲 D:\myplatform\Redis,然後執行命令 redis-server redis.windows.conf 即可啓動redis服務,如圖:
注意:此窗口請勿關閉,否則redis服務就會停止了。
系統服務方式啓動:
cmd窗口下,切換到redis根目錄,執行如下命令:
redis-server --service-install redis.windows-service.conf --loglevel verbose --service-name "Redis Service 4.0" --port 10001
安裝成功後,即可通過windows服務界面的方式啓動redis了。
客戶端測試
新開一個cmd窗口,切換到redis根目錄,執行命令 redis-cli,如果能看到redis的服務器地址和端口,就說明客戶端已成功連接。根據redis啓動方式的不同,選擇相應的命令。
命令行方式:redis-cli
系統服務方式:redis-cli -h 127.0.0.1 -p 10001
安裝php-redis擴展
將php_redis-4.0.2-7.2-ts-vc15-x64.zip壓縮包中的php_redis.dll文件,以及php_igbinary-2.0.7-7.2-ts-vc15-x64.zip壓縮包中的php_igbinary.dll文件,拷貝到php安裝目錄的ext目錄下,如圖:
然後,修改php.ini配置文件,在裏面加入如下擴展:
extension=php_igbinary.dll
extension=php_redis.dll
注意:php_igbinary擴展要放在php_redis擴展的前面。
最後,重啓apache服務。
php客戶端使用
封裝redis工具類,代碼如下:
class RedisUtil {
// redis實例對象
private $redis;
// redis服務器列表
private static $serverList = array();
public function __construct($config=array()) {
$this->initRedis($config);
}
/**
* 初始化redis連接實例,每個redis服務器只連接一次
* @param array $config 參數示例:array('host' => '127.0.0.1','port' => 10001,'timeOut' => 10,'password' => '123456')
* @return mixed
*/
private function initRedis($config=array()) {
if (array_key_exists($config['host'],self::$serverList)) {
return $this->redis;
}
$this->redis = new Redis();
// 連接redis服務端
$this->redis->connect($config['host'], $config['port'],$config['timeOut']);
// 密碼認證
if (isset($config['password']) && $config['password']) {
$this->redis->auth($config['password']);
}
// 連接失敗時,重置$redis實例爲null
if (strcasecmp($this->redis->ping(),'+PONG') != 0) {
$this->redis = null;
}
if ($this->redis) {
self::$serverList[$config['host']] = true;
}
if ($this->redis == null) {
die('Redis 連接異常!');
}
}
/**
* 設置字符串緩存
* @param $key
* @param $value
*/
public function set($key,$value) {
$this->redis->set($key, $value);
}
/**
* 獲取字符串緩存
* @param $key
* @return bool|string
*/
public function get($key) {
return $this->redis->get($key);
}
/**
* 設置隊列緩存(數據左進右出)
* 應用場景:簡單的消息系統、最新列表、秒殺/搶購等
* @param $key 隊列key名稱
* @param $items 隊列數據,支持單個數據和一維數組格式
* @return mixed $items爲單個數據時,返回被添加元素在隊列的第幾位(從1開始)
*/
public function setLeftList($key,$items) {
if (is_array($items)) {
foreach ($items as $item) {
$this->redis->lPush($key, $item);
}
} else {
return $this->redis->lPush($key, $items);
}
}
/**
* 設置隊列緩存(數據右進左出)
* 應用場景:簡單的消息系統、最新列表、秒殺/搶購等
* @param $key 隊列key名稱
* @param $items 隊列數據,支持單個數據和一維數組格式
* @return mixed $items爲單個數據時,返回被添加元素在隊列的第幾位(從1開始)
*/
public function setRightList($key,$items) {
if (is_array($items)) {
foreach ($items as $item) {
$this->redis->rPush($key, $item);
}
} else {
return $this->redis->rPush($key, $items);
}
}
/**
* 從隊列左側彈出一個
* @param $key
* @return mixed
*/
public function popLeft($key) {
return $this->redis->lPop($key);
}
/**
* 從隊列右側彈出一個
* @param $key
* @return mixed
*/
public function popRight($key) {
return $this->redis->rPop($key);
}
/**
* 獲取隊列緩存
* @param $key
* @param int $start 開始位置,默認爲隊列第一個元素
* @param int $end 結束位置,默認爲隊列最後一個元素
* @return mixed
*/
public function getList($key,$start=0,$end=-1) {
return $this->redis->lRange($key, $start, $end);
}
/**
* 獲取隊列中的元素個數
* @param $key
* @return mixed
*/
public function getListSize($key) {
return $this->redis->lLen($key);
}
/**
* 刪除隊列中指定個數的元素值
* @param $key
* @param $value 元素值
* @param int $count 刪除的個數 0 全部 | 正數 從左側刪除指定個數 | 負數 從右側刪除指定個數
*/
public function removeList($key,$value,$count=0) {
$this->redis->lRem($key,$value,$count);
}
/**
* 設置hash表緩存
* 應用場景:對象存儲
* @param $key hash表key名稱
* @param $items hash表數據,一維數組,鍵值對格式,如:array('key1' => 'item1','key2' => 'item2')
*/
public function setHash($key,$items) {
$this->redis->hmset($key, $items);
}
/**
* 獲取hash表緩存
* @param $key hash表key名稱
* @param null $hashKey hash表中的key,省略該參數時,獲取所有數據
* @return mixed
*/
public function getHash($key,$hashKey=null) {
if ($hashKey) {
return is_string($hashKey) ? $this->redis->hGet($key,$hashKey) : $this->redis->hmget($key, $hashKey);
}
return $this->redis->hGetAll($key);
}
/**
* 獲取hash表中的所有key
* @param $key hash表key名稱
* @return mixed
*/
public function getHashKeys($key) {
return $this->redis->hKeys($key);
}
/**
* 刪除hash
* @param $key hash表key名稱
* @param $hashKey hash表中的key,支持單個數據和一維數組
*/
public function removeHash($key,$hashKey) {
if (is_array($hashKey)) {
foreach ($hashKey as $item) {
$this->redis->hDel($key,$item);
}
} else {
$this->redis->hDel($key,$hashKey);
}
}
/**
* 檢查指定的hash表中是否存在某個key
* @param $key hash表key名稱
* @param $hashKey hash表中的key
* @return mixed true 存在 | false 不存在
*/
public function hashExists($key,$hashKey) {
return $this->redis->hExists($key,$hashKey);
}
/**
* 設置set無序集合
* @param $key 集合key名稱
* @param $items 集合數據,支持單個數據和一維數組
*/
public function setCollection($key,$items) {
is_array($items) ? $this->redis->sAddArray($key,$items) : $this->redis->sAdd($key,$items);
}
/**
* 獲取set無序集合
* @param $key
* @return mixed
*/
public function getCollection($key) {
return $this->redis->sMembers($key);
}
/**
* 刪除set無序集合中的元素
* @param $key 集合key名稱
* @param $items 待刪除的元素,支持單個數據和一維數組
*/
public function removeCollection($key,$items) {
if (is_array($items)) {
foreach ($items as $item) {
$this->redis->sRem($key,$item);
}
} else {
$this->redis->sRem($key,$items);
}
}
/**
* 檢查set無序集合中是否包含某個元素
* @param $key 集合key名稱
* @param $item 元素
* @return mixed true 包含 | false 不包含
*/
public function collectionExists($key,$item) {
return $this->redis->sIsMember($key, $item);
}
/**
* 獲取set無序集合中元素的個數
* @param $key 集合key名稱
* @return mixed
*/
public function getCollectionCount($key) {
return $this->redis->sCard($key);
}
/**
* 獲取兩個set無序集合的交集
* @param $key1
* @param $key2
* @return mixed
*/
public function getCollectionInter($key1,$key2) {
return $this->redis->sInter($key1, $key2);
}
/**
* 獲取兩個set無序集合的並集
* @param $key1
* @param $key2
* @return mixed
*/
public function getCollectionUnion($key1,$key2) {
return $this->redis->sUnion($key1, $key2);
}
/**
* 獲取兩個set無序集合的差集
* @param $key1
* @param $key2
* @return mixed
*/
public function getCollectionDiff($key1,$key2) {
return $this->redis->sDiff($key1, $key2);
}
/**
* 設置set有序集合
* @param $key 集合key名稱
* @param $items 集合數據,格式:array('score1' => 'item1','score2' => 'item2')
*/
public function setZCollection($key,$items) {
foreach ($items as $score => $item) {
$this->redis->zAdd($key, $score, $item);
}
}
/**
* 獲取set有序集合
* 應用場景:排行榜
* @param $key 集合key名稱
* @param string $sort 排序方式,asc|desc,默認爲增序
* @param int $start 開始位置,默認爲集合第一個元素
* @param int $end 結束位置,默認爲集合最後一個元素
* @return mixed
*/
public function getZCollection($key,$sort='asc',$start=0,$end=-1) {
if ($sort == 'asc') {
// 成員按分數值遞增排序,分數值相同的則按元素字典序來排序
return $this->redis->zRange($key, $start, $end);
}
// 成員按分數值遞減排序,分數值相同的則按元素字典序的逆序來排序
return $this->redis->zRevRange($key, $start, $end);
}
/**
* 刪除set有序集合中的元素
* @param $key 集合key名稱
* @param $items 待刪除的元素,支持單個數據和一維數組
*/
public function removeZCollection($key,$items) {
if (is_array($items)) {
foreach ($items as $item) {
$this->redis->zRem($key,$item);
}
} else {
$this->redis->zRem($key,$items);
}
}
/**
* 增加set有序集合中指定元素的score
* 應用場景:計數器、統計點擊數等
* @param $key 集合key名稱
* @param $item 元素
* @param int $num 增量,默認爲1
*/
public function zCollectionIncr($key,$item,$num=1) {
$this->redis->zIncrBy($key, $num, $item);
}
/**
* 檢查set有序集合中是否包含某個元素
* @param $key 集合key名稱
* @param $item 元素
* @return bool true 包含 | false 不包含
*/
public function zCollectionExists($key,$item) {
if ($this->redis->zScore($key, $item) !== false) {
return true;
}
return false;
}
/**
* 獲取set有序集合中元素的個數
* @param $key 集合key名稱
* @return mixed
*/
public function getZCollectionCount($key) {
return $this->redis->zCard($key);
}
}
使用方式:
$config = array(
'host' => '127.0.0.1', // redis 服務器地址
'port' => 10001, // redis 服務器端口號
'timeOut' => 10, // redis 客戶端連接超時時間
'password' => '123456' // redis 客戶端連接密碼
);
$redisUtil = new RedisUtil($config);
根據需要,調用相應的方法即可。
Redis相關完整軟件包
下載地址:Redis相關軟件包