目錄
1關於Redis使用的一點想法
1.1進行緩存前,需考慮
(1)該數據屬於短暫保留,例如只保留三天、七天或者一個月,此時建議采用緩存;
(2)該數據在某一個時間段請求量很大,此時建議采用緩存;
(3)隨着用戶使用,數據不斷變化,更新操作比較頻繁,此時建議采用緩存;
(4)如果數據量不大,且和應用性能提升不大,數據需要長久保留,此時不建議采用Redis進行緩存,直接使用MySQL等關系型數據庫存儲即可;
(5)如果數據量很大,但是過了一段時間后,該數據幾乎沒有什么價值,此時建議采用緩存,並設定過期時間和定時清理該數據的腳本,這樣處理可以減輕存儲空間,也便於優化系統的數據庫層。
1.2進行緩存后,需考慮
(1)緩存該key在極端情況下,占用系統內存會有多大?或者說存儲的記錄大概會達到什么數量級?
(2)緩存該key,過期時間是否方便設置?如果不方便設置,是否可以隔段時間考慮轉存到MySQL等關系型數據庫中,從而清理緩存,釋放內存空間。
(3)緩存該key,思考一下手動刪除緩存數據的腳本如何編寫,緩存的所有數據,如何區分出有價值的數據進行保留,無價值的便利用腳本進行自動化刪除。
(4)使用緩存后,要思考選擇恰當的數據結構來完成代碼構建。因為一個適合的數據結構不僅使得代碼變得更加優雅,后期維護也很方便。
1.3緩存使用一段時間后
如果發現某一個key占用內存很大,超出預料,提供的優化建議:
(1)分析該key的具體實際功能,和目前的需求,看能否在后續緩存數據時,添加過期時間設定;
(2)考慮在取出緩存數據的時候,能否轉存到MySQL等關系型數據庫,如果能夠轉存成功,則在此處可以進行立即執行刪除該條緩存數據的方法;后續取數據時,可采用先查詢Redis數據庫,未查到再次查詢一下MySQL等關系型數據庫;
(3)依據已經緩存的數據,看能夠依據數據中的字段或者相關屬性對已經緩存的數據進行過濾查詢,把那些不重要的數據通過腳本進行手動刪除處理。
2編寫Redis數據庫層規范建議
2.1選擇適合的redis客戶端
例如,定義了以下兩個客戶端:
# -*- coding: utf-8 -*-" from django.conf import settings import redis redis_db_client = redis.StrictRedis( host=settings.REDIS['redis_db_host'], port=settings.REDIS['redis_db_port'], db=settings.REDIS['redis_db_db'], socket_connect_timeout=4, socket_timeout=2, decode_responses=True, ) redis_hot_client = redis.StrictRedis( host=settings.REDIS['redis_hot_host'], port=settings.REDIS['redis_hot_port'], db=settings.REDIS['redis_hot_db'], socket_connect_timeout=2, socket_timeout=2, decode_responses=False, )
此時,可以依據key的設計和作用,選擇合適的客戶端來操作。其中不同的客戶端對應的端口和具體數據庫不同,以上客戶端定義僅作參考。(PS:以上定義是基於Django框架的配置文件來使用,其它Python框架也可以類似定義)
2.2規范化定義key的名稱並初始化
在定義redis操作key的名稱建議采用大寫字母加下划線組成;在初始化key對象時,最好能夠設定過期時間。
Key定義的類示例:
class RedisKey: """RedisKey類對象""" def __init__(self, prefix, ex=None): self.prefix = prefix self.ex = ex # 生成key值 def __call__(self, key): return self.prefix + str(key)
則定義一個key的示例:
USER_PULL_URL = RedisKey(prefix='user_pull_url:', ex=8 * 60 * 60)
其中參數prefix為key存儲在Redis數據中具體的鍵名,可以通過調用__call___方法來為key添加后綴,例如user_pull_url:1808表示一個鍵名;ex為該key對象定義的過期時間設定,等到具體編寫該key的添加操作時,調用參數ex來設定key的具體過期時間。
其中key存放的文件,要依據選擇的客戶端存放在指定的文件中,這樣方便查看和管理。
2.3 選擇合適的數據結構
Redis數據庫包含String(字符串)、Hash(哈希表)、List(列表)、Set(集合)、SortedSet(有序集合)五種數據結構。下面簡單介紹一下這五種數據結構的特性:
- String(字符串):添加數據時,采用key-value格式進行存儲。key是定義的鍵名,在上面(2)中已有說明。value是具體要存儲的數據,該數據的類型是String類型。遇到的需求中,例如需要存儲某用戶的在線時長,可以采用key_user_id組成鍵名,具體時長存儲在value中,此時可選擇String數據結構來存儲,比較方便。
- Hash(哈希表):添加數據時,采用key-value格式來進行存儲,不過這里的value表示一張哈希表。可以這樣做比喻,key比作關系型數據庫中的表名,value存儲該關系型表中的所有行的數據記錄。
- List(列表):添加數據時,采用key-value格式來進行存儲,不過這里的value表示一個具體的列表。該列表的功能可類似C++數據結構列表一樣,有出表操作,計算列表長度操作,依據下標獲取某個元素的功能,獲取指定區間內的列表元素,從列表頭部插入元素或者尾部插入元素等。
- Set(集合):添加數據時,采用key-value格式來進行存儲,這里的value存儲和Hash(哈希表)存儲類型,而Set(集合)區別在於在添加數據記錄時,會自動過濾掉相同值得記錄,如果插入多條記錄相同得數據,在Set(集合)存儲得value中只會找到一條記錄。
- SortedSet(有序集合):添加數據時,采用key-value格式來進行存儲。存儲的方式實現功能和Set(集合)基本相同,但是其唯一突出的特點就是在存數據的時候要存儲元素值對應的score值,也就是依據score值的大小來對value中元素進行排序存儲。后續有查詢操作時,可以很方便的返回value中元素的排序序列。
2.4 規范化定義操作方法
每一個服務層,建議單獨創建一個cache.py文件,專門用於存放操作Redis數據庫層的方法,此類的功能可以類比models.py文件。
每一個key使用其鍵名創建符合代碼規范的類名,然后在該key對應的類里面,定義操作的redis_client和redis_key,最后通過cliet和key定義相關數據的添加、修改、查詢和刪除的方法。
最后,最重要的一點建議:該cache.py中定義的操作方法建議只在該服務層中被其它類中方法體調用。這樣的好處,可以讓我們對於該key在以后的數據管理上有可控的預估操作,也使得代碼調用變得更加規范。
此處給出一個示例:
定義一個key:
鍵名初始化:
USER_SESSION = RedisKey(prefix='user_session:', ex=4 * 60 * 60)
選擇客戶端:
redis_hot_client
在cache.py中定義的類和相關操作方法:
class CacheUserSession: # 類名和key的名稱對應 """ 原始數據類型: dict 存儲數據類型: bytes 數據說明:將user_dict序列化成二進制數據,存入Redis中 """ db = redis_hot_client key_prefix = rediskey.USER_SESSION @classmethod def get(cls, user_id: IdInt): key = cls.key_prefix(user_id) # key的具體初始化值 user_dict = cls.db.get(key) if user_dict: user_dict = pickle.loads(user_dict) return user_dict @classmethod def delete(cls, user_id: IdInt): # key的刪除方法定義 key = cls.key_prefix(user_id) cls.db.delete(key) logger.info("Delete UserSession:{}".format(user_id)) @classmethod def set(cls, user_id: IdInt, user_dict: dict): key = cls.key_prefix(user_id) data = pickle.dumps(user_dict) cls.db.set(key, data, ex=cls.key_prefix.ex) # 注意設定過期時間 logger.info("Set UserSession:{}, {}".format(user_id, user_dict))
此處,給出一個很好用的存儲數據的途徑。通過以下途徑,可以把一個字典格式的數據當作字符串存入Redis數據庫,取出后可以重新解析出字典格式。通過該途徑,可以較好的把Redis當作關系型數據庫存儲一樣,一條數據記錄可以存儲多個屬性的值。
例如,一個學生,包含學號、姓名、性別、年級、專業等屬性。如果采用關系型數據庫存,以學號作為主鍵,其它屬性作為列名進行設計,通過學號即可查詢出該學生的所有信息。但是,一個學生的多個屬性信息如何存儲到Redis數據庫中,並且取出來能夠很好的使用呢?
采用的策略:學號唯一,可作為key值,姓名、性別、年級、專業聯合成一個字典,在存入Redis數據中之前先轉換為指定結構的字符串格式,取出后再解析成字典格式。這樣操作就能很好的解決這個需求。
為什么要轉換為字符串格式存入Redis數據庫呢?因為不管什么數據結構的數據存入到Redis數據庫中后,它取出都是字符串格式。
下面請看具體代碼示例(具體cache.py中CacheStudent類實現代碼不給出噢):
import json
value_data = json.dumps({ name: "xiaoming", gender: "male", grade: "2014", profession: "軟件工程", }) # 通過json模塊把字典轉換為指定格式的字符串 CacheStudent.set(student_id, value_data) # 通過定義好的寫入方法,把指定的學生數據存入 student_data = json.loads(CacheStudent.get(student_id)) # 取出指定學號學生的數據后,使用json模塊的loads方法解析該字符串變成字典格式 print(studnet_data["name"]) # 打印該學生的姓名信息 print(studnet_data["gender"]) print(studnet_data["grade"]) print(studnet_data["profession"])
看到上述的實現,是不是發現Redis存儲可以當作關系型數據存儲來用?
2.5 開始愉快的調用之旅
此處調用,可適當選擇時機何時進行數據刪除操作。比如取出后,可以選擇把最終存儲的數據轉存到MySQL數據庫中,然后調用cache.py中的刪除方法,對該數據記錄進行刪除操作。