利用Redis cache優化app查詢速度實踐

雲巴發表於2015-09-10

注意:本篇文章譯自speeding up existing app with a redis cache,如需要轉載請註明出處。

發現問題

在應用解決方法之前,我們需要對我們面對的問題有一個清晰的認識。
App所遇到的問題是,當執行一個查詢時,它會跑到Diffbot’s API 然後查詢資料集。子集被返回並展示出來。根據Diffbot伺服器的繁忙程度,可能需要花5秒左右的時間去完成這一過程。如果擴充套件計算機的能力這種情形無疑會改進,如果一個查詢執行一次就被記住並且重複使用24小時,通常可以把這個過程看成重新整理這個集合,並且這個方式會非常的高效。

你可能會懷疑“快取一個查詢有什麼好處呢?”大多數人應該都不會只查詢一個東西或者同樣的事物。

呃…事實上,不僅調查表明人們經常查詢一個事情或相同的事,他們通常也會去搜尋多產作家(或自己)。考慮到事實上應用這一快取方式並沒有增加紙面上的成本(其實是通過減少伺服器壓力而減少成本),把這個加進來是一個容易的盈利點,即使它使用頻率並沒有我們希望那樣高。但我們也沒有任何理由不使用它—-因為它可以給我們帶來利益。

既然問題已經界定清楚,讓我們先處理先決條件。

配置環境

首先,我們需要在開發和生產環境下安裝Redis(需要注意的是,如果你把Homestead用於本地開發,Redis就已經安裝好了,目前使用的是v3.0.1版本)

我們可以通過作業系統的包管理器來做這件事:

sudo apt-get install redis-server

這是最簡單也是最為推薦的方法,但我們也可以從頭來安裝並且手動配置。根據他們網上的說明,我們可以如此配置:

sudo apt-get install gcc make build-essential tcl
wget http://download.redis.io/releases/redis-3.0.2.tar.gz
tar xzf redis-3.0.2.tar.gz
cd redis-3.0.2
make
make test
sudo make install

如果你執行make遇到錯誤提示jemalloc.h那麼執行make distclean然後在執行makemake test命令是選擇性執行的,但是很有幫助。

注意:如果你看到這裡,而3.0.2已經不是最新的版本,那麼根據你的最新版本號去調節命令。

為了防止一些常見的警告(至少在Ubuntu上),我們還需要預防性的執行以下命令:

sudo sh -c `echo "vm.overcommit_memory=1" >> /etc/sysctl.conf`
sudo sh -c `echo "net.core.somaxconn=65535" >> /etc/sysctl.conf`
sudo sh -c `echo "never" > /sys/kernel/mm/transparent_hugepage/enabled`

我們也要確保最後的命令在exit 0上被新增到了/etc/rc.local,因此能保證在每個重啟的伺服器上能重新傳送。最後我們可以用sudo reboot重啟伺服器並且執行有sudo redis-server的Redis檢查是否一切正常。

最後,我們要確保在伺服器重啟後Redis會啟動,所以我們要跟著官方的說明去完成配置。

Predis

我們之前說了一些關於Predis的基礎知識,我們將要將其用到本文的例子中:

composer require predis/predis

進一步的,假設我們已經瞭解之前敘述的關於Predis的知識。

和之前發表的關於Predis相比,雖然是有一些不同(比如過渡到名稱空間),但我們需要的API幾乎是一樣的。

實施
要在我們app裡運用Redis,我們需要遵循以下的程式:
 檢視當前的快取中是否有查詢結果
 如果是,抓取他們
 如果沒有,把他們拿來,儲存,將他們傳送到app的其他部分

因此,實施非常的簡單:在“form submitted”下檢查(尋找“search”引數),我們例項化Predis客戶端,計算search查詢的MD5 hash值,然後檢查查詢結果是否已經被快取。如果失敗,就在重複前面的流程。

$result = ...
$info = ...

我們將查詢結果序列化並直接儲存到cache裡。然後我們在模組外立即抓取他們,app的流程就和往常一樣繼續。而index.php改變的部分如下:

// Check if the search form was submitted
if (isset($queryParams[`search`])) {

    $redis = new Client();
    $hash = md5($_SERVER[`QUERY_STRING`]);
    if (!$redis->get($hash . `-results`)) {

        $diffbot = new Diffbot(DIFFBOT_TOKEN);

        // Building the search string
        $searchHelper = new SearchHelper();
        $string = (isset($queryParams[`q`]) && !empty($queryParams[`q`]))
            ? $queryParams[`q`]
            : $searchHelper->stringFromParams($queryParams);

        // Basics
        $search = $diffbot
            ->search($string)
            ->setCol(`sp_search`)
            ->setStart(($queryParams[`page`] - 1) * $resultsPerPage)
            ->setNum($resultsPerPage);

        $redis->set($hash . `-results`, serialize($search->call()));
        $redis->expire($hash . `-results`, 86400);
        $redis->set($hash . `-info`, serialize($search->call(true)));
        $redis->expire($hash . `-info`, 86400);
    }

    $results = unserialize($redis->get($hash . `-results`));
    $info = unserialize($redis->get($hash . `-info`));

進過測試,我們可以看到它的魅力所在—如果我們重新整理頁面,或執行另一個查詢,就會立即執行一次查詢,然後會回到之前的那個。最後我們新增,提交,推動部署一下內容:

git add -A
git commit -m "Added Redis cache [deploy:production]"
git push origin master

就是這麼簡單,我們的最新版的app已經上線,而且使用的Redis。

注意:如果你想知道我們是如何用一條命令從開發模式轉到生產部署,你可以看這裡

微調
為了進一步的提升效能,Predis推薦安裝phpiredis,這是個PHP的擴充套件,目的是“降低序列化和解析Redis協議的成本”。可以看作我們完全控制了伺服器,有什麼理由不試試呢?

cd ~
git clone https://github.com/redis/hiredis
cd hiredis
make
sudo make install
cd ~
git clone https://github.com/nrk/phpiredis
cd phpiredis
phpize && ./configure --enable-phpiredis
make
sudo make install

sudo touch /etc/php5/mods-available/phpiredis.ini
sudo sh -c `echo "extension=phpiredis.so" > /etc/php5/mods-available/phpiredis.ini`
sudo php5enmod phpiredis
sudo service php5-fpm restart

以上是安裝的前提,並且啟用了擴充套件。現在我們要做的就是利用phpiredis連結去配置Predis客戶端。因此我們需要更換:

$redis = new Client();

$redis = new Client(`tcp://127.0.0.1`, [
        `connections` => [
            `tcp`  => `PredisConnectionPhpiredisStreamConnection`,
            `unix` => `PredisConnectionPhpiredisSocketConnection`,
        ],
    ]);

就是這麼簡單!現在我們的Redis安裝會更快!

總結:

在本教程中,我們利用Redis結合Predis庫來提升已部署的app的速度,我們平衡大資料海洋的水滴中可用的RAM來儲存每天一次查詢的結果,然後從快取中返回這些結果,而不是重新執行一遍查詢。但這確實意味著結果不會總是最新的,但就這邊文章,其實查詢結果沒有被重新整理的次數比這種情況多得多。

注:關於更多的有關Redis的知識可以參考redisdoc.com (此網站文件是 Redis Command Reference 和 Redis Documentation 的中文翻譯版, 閱讀這個文件可以幫助你瞭解 Redis 命令的具體使用方法, 並學會如何使用 Redis 的事務、持久化、複製、Sentinel、叢集等功能。)
我們雲巴的產品也是使用redis儲存實踐,大家也可以來交流學習~

相關文章