# 緩存
### 緩存的原則
緩存是一個大型系統中非常重要的一個組成部分。在硬件層面,大部分的計算機硬件都會用緩存來提高速度,比如CPU會有多級緩存、RAID卡也有讀寫緩存。在軟件層面,我們用的數據庫就是一個緩存設計非常好的例子,在SQL語句的優化、索引設計、磁盤讀寫的各個地方,都有緩存,建議大家在設計自己的緩存之前,先去了解下MySQL里面的各種緩存機制,感興趣的可以去看下[High Permance MySQL](http://www.highperfmysql.com/)這本非常有價值的書。
一個生產環境的緩存系統,需要根據自己的業務場景和系統瓶頸,來找出最好的方案,這是一門平衡的藝術。
一般來說,緩存有兩個原則。**一是越靠近用戶的請求越好**,比如能用本地緩存的就不要發送HTTP請求,能用CDN緩存的就不要打到web服務器,能用nginx緩存的就不要用數據庫的緩存;**二是盡量使用本進程和本機的緩存解決**,因為跨了進程和機器甚至機房,緩存的網絡開銷就會非常大,在高并發的時候會非常明顯。
### OPenResty的緩存
我們介紹下在OpenResty里面,有哪些緩存的方法。
#### 使用[lua shared dict](http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT)
我們看下面這段代碼:
~~~
function get_from_cache(key)
local cache_ngx = ngx.shared.my_cache
local value = cache_ngx:get(key)
return value
end
function set_to_cache(key, value, exptime)
if not exptime then
exptime = 0
end
local cache_ngx = ngx.shared.my_cache
local succ, err, forcible = cache_ngx:set(key, value, exptime)
return succ
end
~~~
這里面用的就是ngx shared dict cache。你可能會奇怪,ngx.shared.my_cache是從哪里冒出來的?沒錯,少貼了nginx.conf里面的修改:
~~~
lua_shared_dict my_cache 128m;
~~~
如同它的名字一樣,這個cache是nginx所有worker之間共享的,內部使用的LRU算法(最近經常使用)來判斷緩存是否在內存占滿時被清除。
#### 使用[lua LRU cache](https://github.com/openresty/lua-resty-lrucache)
直接復制下春哥的示例代碼:
~~~
local _M = {}
-- alternatively: local lrucache = require "resty.lrucache.pureffi"
local lrucache = require "resty.lrucache"
-- we need to initialize the cache on the lua module level so that
-- it can be shared by all the requests served by each nginx worker process:
local c = lrucache.new(200) -- allow up to 200 items in the cache
if not c then
return error("failed to create the cache: " .. (err or "unknown"))
end
function _M.go()
c:set("dog", 32)
c:set("cat", 56)
ngx.say("dog: ", c:get("dog"))
ngx.say("cat: ", c:get("cat"))
c:set("dog", { age = 10 }, 0.1) -- expire in 0.1 sec
c:delete("dog")
end
return _M
~~~
可以看出來,這個cache是worker級別的,不會在nginx wokers之間共享。并且,它是預先分配好key的數量,而shared dcit需要自己用key和value的大小和數量,來估算需要把內存設置為多少。
#### 如何選擇?
shared.dict 使用的是共享內存,每次操作都是全局鎖,如果高并發環境,不同worker之間容易引起競爭。所以單個shared.dict的體積不能過大。lrucache是worker內使用的,由于nginx是單進程方式存在,所以永遠不會觸發鎖,效率上有優勢,并且沒有shared.dict的體積限制,內存上也更彈性,但不同worker之間數據不同享,同一緩存數據可能被冗余存儲。
你需要考慮的,一個是lua lru cache提供的API比較少,現在只有get、set和delete,而ngx shared dict還可以add、replace、incr、get_stale(在key過期時也可以返回之前的值)、get_keys(獲取所有key,雖然不推薦,但說不定你的業務需要呢);第二個是內存的占用,由于ngx shared dict是workers之間共享的,所以在多worker的情況下,內存占用比較少。
- 序
- Lua簡介
- Lua環境搭建
- 基礎數據類型
- 表達式
- 控制結構
- if/else
- while
- repeat
- 控制結構for的使用
- break,return
- Lua函數
- 函數的定義
- 函數的參數
- 函數的返回值
- 函數回調
- 模塊
- String庫
- Table庫
- 日期時間函數
- 數學庫函數
- 文件操作
- 元表
- 面向對象編程
- FFI
- LuaRestyRedisLibrary
- select+set_keepalive組合操作引起的數據讀寫錯誤
- redis接口的二次封裝(簡化建連、拆連等細節)
- redis接口的二次封裝(發布訂閱)
- pipeline壓縮請求數量
- script壓縮復雜請求
- LuaCjsonLibrary
- json解析的異常捕獲
- 稀疏數組
- 空table編碼為array還是object
- 跨平臺的庫選擇
- PostgresNginxModule
- 調用方式簡介
- 不支持事務
- 超時
- 健康監測
- SQL注入
- LuaNginxModule
- 執行階段概念
- 正確的記錄日志
- 熱裝載代碼
- 阻塞操作
- 緩存
- sleep
- 定時任務
- 禁止某些終端訪問
- 請求返回后繼續執行
- 調試
- 調用其他C函數動態庫
- 我的lua代碼需要調優么
- 變量的共享范圍
- 動態限速
- shared.dict 非隊列性質
- 如何添加自己的lua api
- 正確使用長鏈接
- 如何引用第三方resty庫
- 使用動態DNS來完成HTTP請求
- 緩存失效風暴
- Lua
- 下標從1開始
- 局部變量
- 判斷數組大小
- 非空判斷
- 正則表達式
- 不用標準庫
- 虛變量
- 函數在調用代碼前定義
- 抵制使用module()函數來定義Lua模塊
- 點號與冒號操作符的區別
- 測試
- 單元測試
- API測試
- 性能測試
- 持續集成
- 灰度發布
- web服務
- API的設計
- 數據合法性檢測
- 協議無痛升級
- 代碼規范
- 連接池
- c10k編程
- TIME_WAIT問題
- 與Docker使用的網絡瓶頸
- 火焰圖
- 什么時候使用
- 顯示的是什么
- 如何安裝火焰圖生成工具
- 如何定位問題