10分鐘內學會快取機制

10分鐘內學會快取機制

這篇文章我想和你分享如何透過快取來提升你的 API 效能,因為每一位後端工程師都會被要求要提升系統的效能

請你看看左邊這張圖,這是電商常見的一種行銷操作

https://metaschool.asia/storage/articles/segments/180-2.jpeg

假如行銷人員希望這張圖表是變動的,需要用程式動態算出商品排名的話,該怎麼做呢?

作法需要結合 item_order 中介表格以及 items 商品表格來查詢出銷售量最多的 10 個商品,

算起來最少就要做兩張表格的查詢,而且還沒算到中間的商業邏輯運算

如果需要用到 orders 表格的資料,那 loading 就變得更多,而且還是每一次有人請求這張表就要做這些計算,這就是方案A的做法

但其實也可以考慮方案B ,就是直接從快取去抓取資料,就像是數學考試時看到題目不用算

直接把背好的答案寫上去,就可以節省掉計算所花費的時間囉

那究竟該如何去實作快取呢?答案就是結合方案A與方案B

https://metaschool.asia/storage/articles/segments/180-3.jpeg

當快取不存在資料時,先透過方案A來算出答案,答案出來後存入快取中。之後當有其他人需要這份資料時,就一律透過快取來提供,直到快取過期或者是被刪除為止

再繼續討論更多實作的細節之前,我想先和你聊另一個話題

就是 Laravel 快取技術的底層是由什麼實作出來的?

其實 Laravel 框架內建支援很多種的快取機制,這邊我想要向你說明最常見的三種

第一種是透過檔案來儲存,驅動名稱為 file,其實這也是 Laravel 所內建的快取機制

如果你不做任何改動的使用快取,使用的就是 file 驅動

第二種是透過資料庫表格來儲存,驅動名稱為 database

經過我的測試它的效能比檔案來的更高,但你需要先生成快取表格才能夠啟用喔

資料庫快取啟動流程

步驟1.建立 migration

php artisan cache:table

步驟2.執行 migrate

php artisan migrate

第三種是透過 Redis 服務來儲存,驅動名稱為 redis

Redis 是一種知名的鍵值對形式的資料庫,廣泛地用於中大型系統的快取服務,因為需要花費成本才能去架設使用,多用於已經上線且對效能有更多要求的系統上

那究竟該如何切換成其他驅動呢?答案非常的簡單!

你看一下快取設定檔裡的 default 設定,它會先去看隱私設定檔,如果不存在就指定為 file

而 file 的檔案路徑就列在下方,如果你有興趣的可以看一下

//config\cache.php

'default' => env('CACHE_DRIVER', 'file'),

'stores' => [
                ...
        'database' => [
            'driver' => 'database',
            'table' => 'cache',
            'connection' => null,
            'lock_connection' => null,
        ],

        'file' => [
            'driver' => 'file',
            'path' => storage_path('framework/cache/data'),
        ],

             'redis' => [
            'driver' => 'redis',
            'connection' => 'cache',
            'lock_connection' => 'default',
        ]
]

而 Laravel 新建專案的 .env 檔案裡頭,CACHE_DRIVER的 值就是 file,如果你想要改成其他驅動

只要改這個鍵的值即可

//.env

CACHE_DRIVER=database

效能優化實測

為了實地的了解快取能帶來的效能提升,我做了一些測試,歡迎一起來看看這些測試的數據

未使用快取圖

https://metaschool.asia/storage/articles/segments/180-5.png

導入檔案(file)來進行快取,這一次有百分之九十的請求都是在 78 微秒內完成,

縮短了大約 22 % 的請求時間

https://metaschool.asia/storage/articles/segments/180-6.png

導入資料庫(database)來進行快取,有百分之九十的請求都是在 65 微秒內完成,縮短了大約 35 % 的請求時間

https://metaschool.asia/storage/articles/segments/180-7.png

如果你好奇我是怎麼進行測試的,我的測試作法如下:

步驟 1

修改 .env 檔案

//.env

APP_DEBUG=false

步驟2

開啟 Terminal ,執行以下指令

ab -t 10 -c 10 {應用首頁網址}

Cache Facade 介紹

這邊我來介紹一些比較常用的函式

has() 用來判斷某快取資料是否存在,你必須要確認該快取存在才能夠取用

forever() 用來永久地將資料存入快取中,該資料永遠不會過期,但你可以手動的清除這些資料

get() 用來取得某快取資料

flush() 可以一次性的清除所有自訂的快取資料

導入快取程式碼

//app\Http\Controllers\SiteController.php

public function renderNewsPage(Request $request)
    {
                //判斷快取是否具有該資料
        if (!Cache::has('staticPageCache_news')) {
                        //沒有的話,就進行查詢或運算
            $query = Article::where('status', 'published');
            $cgy_id = null;
            $keyword = null;
            if ($request->has('cgy_id')) {
                $cgy_id = $request->cgy_id;
                $query = $query->where('cgy_id', $cgy_id);
            } else {
                $ids = Cgy::find(21)->getChildIds();
                $query = $query->whereIn('cgy_id', $ids);
            }

            $articles = $query->paginate(3);
            $cgies = Cgy::where('parent_id', 21)->enabled(true)->get();

            $data = compact('articles', 'cgies', 'cgy_id', 'keyword');
                        //將資料永久的存入快取之中
            Cache::forever('staticPageCache_news', $data);
        }
                //將資料從快取中取出
        return view('news', Cache::get('staticPageCache_news'));
    }

程式碼大概分為四段:

第一段先判斷快取是否存在

第二段如果快取不存在就進行查詢或運算

第三段在查詢或運算後將結果存入快取

第四段將快取資料傳入到視圖內

清除快取程式碼

//app\Http\Controllers\SiteController.php

//清除頁面快取
public function clearPageCache()
{
        //只有管理員才可以清除快取
    if (Auth::user()->role->name == 'admin') {
        Cache::flush();
        return '頁面 Cache 已經清除完畢';
    } else {
        return redirect(url('/'));
    }
}
//routes\web.php

Route::namespace('App\Http\Controllers')->group(function () {
    ...
    Route::get('/clearcache', 'SiteController@clearPageCache');
});

導入快取之後,所有的查詢運算都只會進行一次,之後都是轉由快取來提供,最終達成我們想要的系統效能提升

但有個問題是當資料被變更時,前端得到的依然會是快取的資料,因此需要搭配手動清除快取的機制

我的作法是會設計一個只提供給管理員去使用的路,當資料變更時就清除所有的快取,就能夠確保前端可以得到最新的資料囉


分享這篇文章:

訂閱電子報,索取 Laravel 學習手冊

價值超過 3000 元,包含常用 Laravel 語法與指令!

一小時免費求職講座

3個應徵軟體工程師前該知道的秘訣

取得免費課程連結

Laravel 百萬年薪特訓營

從最基礎的 PHP 語法開始,包含所有你該知道的網頁基礎知識,連同 Laravel 從零開始一直到實戰,最後還將告訴你如何找好工作,讓你及早擁有百萬年薪