注意:本篇文章譯自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' => 'Predis\Connection\PhpiredisStreamConnection', 'unix' => 'Predis\Connection\PhpiredisSocketConnection', ], ]);
就是這么簡單!現在我們的Redis安裝會更快!
總結:
在本教程中,我們利用Redis結合Predis庫來提升已部署的app的速度,我們平衡大數據海洋的水滴中可用的RAM來存儲每天一次查詢的結果,然后從緩存中返回這些結果,而不是重新運行一遍查詢。但這確實意味着結果不會總是最新的,但就這邊文章,其實查詢結果沒有被刷新的次數比這種情況多得多。
注:關於更多的有關Redis的知識可以參考redisdoc.com (此網站文檔是 Redis Command Reference 和 Redis Documentation 的中文翻譯版, 閱讀這個文檔可以幫助你了解 Redis 命令的具體使用方法, 並學會如何使用 Redis 的事務、持久化、復制、Sentinel、集群等功能。)
我們雲巴的產品也是使用redis存儲實踐,大家也可以來交流學習~