注意:本篇文章譯自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
然後在執行make
。make 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儲存實踐,大家也可以來交流學習~