Redis實(shí)現(xiàn)列表數(shù)據(jù)查詢?cè)O(shè)計(jì)
文章簡(jiǎn)介
本文總結(jié)個(gè)人在使用Redis存儲(chǔ)列表數(shù)據(jù)業(yè)務(wù)場(chǎng)景下的一些思路。平常在使用數(shù)據(jù)查詢時(shí),我們一般會(huì)將查詢出來(lái)的數(shù)據(jù)使用json_encode()序列化一下,然后根據(jù)數(shù)據(jù)ID存儲(chǔ)到Redis中。這樣針對(duì)列表類的數(shù)據(jù),或許就不是很好的實(shí)現(xiàn)了(因?yàn)樯婕暗椒猪?yè)計(jì)算)。本文使用String和zset類型實(shí)現(xiàn)這樣的功能。
數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)
上圖為zset緩存數(shù)據(jù)ID,和String緩存實(shí)際信息的一個(gè)映射關(guān)系。
zset中的分?jǐn)?shù)和值都是數(shù)據(jù)的ID,是因?yàn)閿?shù)據(jù)的ID是唯一的,zset中的值和分?jǐn)?shù)也是唯一的。正好符合這種關(guān)系。
String存儲(chǔ)實(shí)體信息。緩存key則以數(shù)據(jù)ID作為鍵名,值為序列化后的數(shù)據(jù)信息。
zset中的值和String緩存中的key一一映射。
接口數(shù)據(jù)處理
接口獲取數(shù)據(jù)一般就是傳遞一個(gè)頁(yè)碼(page)和一個(gè)分頁(yè)大小(size)。我們先去zset中獲取對(duì)應(yīng)的ID。然后根據(jù)ID依次獲取String中的數(shù)據(jù)。如何根據(jù)分頁(yè)去讀取zset中的ID呢?可以根據(jù)下面的公式:
// 開(kāi)始位置
$start = ($page - 1) * $size;
// 結(jié)束位置
$end = $start + $size;
// 獲取ID
$idArray = $redis->zRange($key, $start, $end);
var_dump($idArray);
// output
[1, 2, 4, 5, 6]
// 根據(jù)ID向Redis獲取數(shù)據(jù)
$returnArray = [];
foreach ($idArray as $key => $value) {
$returnArray[$key] = json_decode('cache:' . $value, true);
}
return $returnArray;
后臺(tái)數(shù)據(jù)維護(hù)
用戶端獲取數(shù)據(jù)解決了,如果后臺(tái)數(shù)據(jù)變更了,該如何處理呢?這里只要我們對(duì)后臺(tái)的數(shù)據(jù)做了操作,去操作對(duì)應(yīng)的緩存即可。整體思路如下:
代碼演示
Redis連接
class RedisConnection
{
public $redisConnection;
public function __construct()
{
$redis = new Redis();
$redis->connect('192.168.2.102', 6379, 1);
$redis->auth(6379);
$this->redisConnection = $redis;
}
}
MySQL連接
class DBConnection
{
/**
* 數(shù)據(jù)庫(kù)類型
* @var string
*/
private $dbms = 'mysql';
/**
* 主機(jī)地址
* @var string
*/
private $host = 'mysql5';
/**
* 端口號(hào)
* @var int
*/
private $port = 3306;
/**
* 數(shù)據(jù)庫(kù)名稱
* @var string
*/
private $dbName = 'tools';
/**
* 用戶名稱
* @var string
*/
private $user = 'root';
/**
* 密碼
* @var string
*/
private $pass = '123456';
/**
* 連接對(duì)象
* @var PDO
*/
public $dbConnection;
public function __construct()
{
try {
$dbh = new PDO("{$this->dbms}:host={$this->host};port:{$this->port};dbname={$this->dbName}",
$this->user,
$this->pass, [
PDO::ATTR_PERSISTENT => true
]);
$this->dbConnection = $dbh;
} catch (Exception $exception) {
var_dump('MySQL連接異常:' . $exception->getMessage());
}
}
}
管理端
class Manage
{
/**
* Redis連接對(duì)象
* @var Redis
*/
private $redis;
/**
* MySQL連接對(duì)象
* @var PDO
*/
private $db;
public function __construct()
{
$this->db = (new DBConnection())->dbConnection;
$this->redis = (new RedisConnection())->redisConnection;
}
/**
* 數(shù)據(jù)增加
* @return void
*/
public function insert()
{
// 添加的數(shù)據(jù)
$data = [];
// 1. 插入數(shù)據(jù)庫(kù)
$insertId = 0;
// 2. 插入Redis
// 2.1 插入string
$this->redis->set('cache:' . $insertId, json_encode($data));
// 2.2 插入zset
$this->redis->zAdd('list', [], $insertId, $insertId);
}
/**
* 數(shù)據(jù)更新
* @return void
*/
public function update()
{
// 更新數(shù)據(jù)
$data = [];
// 1. 更新數(shù)據(jù)庫(kù)
$updatedId = 0;
// 2. 更新Redis
$this->redis->set('cache:' . $updatedId, json_encode($data));
}
/**
* 數(shù)據(jù)刪除
* @return void
*/
public function delete()
{
// 1.刪除數(shù)據(jù)id
$deleteId = 0;
// 2.刪除Redis
// 2.1刪除string
$this->redis->del('cache:' . $deleteId);
// 2.2刪除zset
$this->redis->zDelete('list', $deleteId);
}
}
接口端
class Api
{
/**
* Redis連接對(duì)象
* @var Redis
*/
private $redis;
public function __construct()
{
$this->redis = (new RedisConnection())->redisConnection;
}
/**
* 獲取列表
* @return array
*/
public function list()
{
/**
* 這里羅列普通的查詢
* 如果涉及到條件查詢,可以先根據(jù)條件去MySQL中查詢到主表的ID。在根據(jù)ID走這樣的邏輯。
*/
// 頁(yè)碼
$page = 1;
// 分頁(yè)大小
$size = 10;
// 1.先根據(jù)分頁(yè)獲取zset中的id(分?jǐn)?shù))
$start = ($page - 1) * $size;
$idArray = $this->redis->zRange('list', $start, $size + $start);
// 2.根據(jù)獲取到的id,去string中查找
$returnArray = [];
foreach ($idArray as $key => $value) {
$returnArray[$key] = json_decode('cache:' . $value, true);
}
return $returnArray;
}
// 獲取詳情
public function find()
{
// 客戶端請(qǐng)求的,數(shù)據(jù)id
$id = 1;
return json_decode($this->redis->get('cache:' . $id), true);
}
}
問(wèn)題總結(jié)
列表參數(shù)化查詢?nèi)绾翁幚恚?/section>
列表數(shù)據(jù)一般都是有傳遞用戶查詢參數(shù),這時(shí)候我們可以實(shí)現(xiàn)根據(jù)條件去數(shù)據(jù)庫(kù)篩選出對(duì)應(yīng)的數(shù)據(jù)ID,并且只查詢ID即可,然后根據(jù)ID去執(zhí)行上面的邏輯。
