[TOC]
# Lazyfree
大key刪除的問題想必很多用戶都遇到過,Redis除string外還支持list、set、hash和sorted set等復雜數據結構,這些數據結構豐富了Redis的用法,但是如果使用不當造成單key體積過大的話就會引起一些問題。
舉個簡單的例子,假如某社交網站有一個大V,有上百萬的粉絲,我們可以用set集合類型的數據結構來存儲他的粉絲ID,存儲粉絲集合的key叫做funs好了,我們來看下粉絲數:
~~~
127.0.0.1:6379> SCARD funs
(integer) 6320505
~~~
的確是大V,有600多萬的粉絲,但是很不幸的有一天這個大V注銷了,這時就要刪除他的信息,我們用DEL命令來刪除這個key:
~~~
127.0.0.1:6379> DEL funs
(integer) 1
(3.11s)
127.0.0.1:6379> slowlog get
1) 1) (integer) 4
2) (integer) 1528169923
3) (integer) 3104812
4) 1) "DEL"
2) "funs"
5) "127.0.0.1:48398"
6) ""
~~~
* 小插曲:Redis 4.0擴展了slowlog的返回結果,展示了產生慢日志的客戶端IP:PORT以便追本溯源。
可以看到刪除這個動作居然耗時3秒多,也就意味著這3秒內Redis無法執行其他命令,這對于線上業務來講是有傷害的,那么如何避免刪除大key時的阻塞問題呢?Redis 4.0推出了Lazyfree這一功能,使用UNLINK命令來刪除大key,主線程只負責把key從數據庫中"摘除",真正的釋放動作放在了BIO后臺線程去做,我們來看下效果:
~~~
127.0.0.1:6379> UNLINK funs
(integer) 1
(3.11s)
127.0.0.1:6379> slowlog get
(empty list or set)
~~~
可以看到UNLINK執行很快沒有產生slowlog。
Lazyfree一共有3個命令:
1. UNLINK:異步刪除key
2. FLUSHDB ASYNC:異步清空當前DB
3. FLUSHALL ASYNC:異步清空所有DB
以及4個配置項:
1. lazyfree-lazy-expire:異步刪除過期key
2. lazyfree-lazy-eviction:異步淘汰key
3. lazyfree-lazy-server-del:隱式刪除時采取異步刪除,比如rename a b,若b存在則需刪除b
4. slave-lazy-flush:全量同步時,slave異步清空所有DB
對于源碼實現有興趣的讀者可以閱讀
https://yq.aliyun.com/articles/205504?spm=a2c4e.11153940.blogcont600648.12.36ab1b52lTEvxh
# Lua腳本支持隨機操作
Redis內嵌了Lua環境來支持用戶擴展功能,但是出于數據一致性考慮,要求腳本必須是純函數的形式,也就是說對于一段Lua腳本給定相同的參數,重復執行其結果都是相同的。
為什么要有這個限制呢?原因是Redis不僅僅是單機版的內存數據庫,它還支持主從復制和持久化,執行過的Lua腳本會復制給slave以及持久化到磁盤,如果重復執行得到結果不同,那么就會出現內存、磁盤、slave之間的數據不一致,在failover或者重啟之后造成數據錯亂影響業務。
還是以具體例子來看,假設有這么一段Lua腳本,目的很簡單就是想記錄下當前時間:
~~~
local now = redis.call('time')[1]
redis.call('set','now',now)
return redis.call('get','now')
~~~
這里使用了Redis的TIME命令來獲取時間戳,然后存儲到名為now的key中,但是其執行時會報錯:
~~~
$redis-cli --eval escript
(error) ERR Error running script (call to f_cfba5ec6a699dad183456f19d1099d8dabfdb80c):
@user_script:3: @user_script: 3: Write commands not allowed after non deterministic commands.
Call redis.replicate_commands() at the start of your script in order to switch to single commands replication mode.
~~~
錯誤提示也很明顯,如果執行過非確定性命令(也就是TIME,因為時間是隨機的),Redis就不允許執行寫命令,以此來保證數據一致性。那如何才能實現隨機寫入呢?剛才的錯誤提示也給出了答案,使用redis.replicate_commands(),在執行redis.replicate_commands()之后,Redis就不再是把整個Lua腳本同步給slave和持久化,而是把腳本中調用Redis的寫命令直接去做復制,那么slave和持久化也可以得到確定的結果。
腳本修改如下:
~~~
redis.replicate_commands()
local now = redis.call('time')[1]
redis.call('set','now',now)
return redis.call('get','now')
~~~
再執行就可以實現隨機寫入了:
~~~
$redis-cli --eval escript
"1528191578"
$redis-cli --eval escript
"1528191804"
~~~
# 基于LFU的熱點key發現機制
LFU是Redis 4.0新增的一類內存逐出策略,提供了更精確的內存淘汰算法,其本質是記錄了一段時間內key的訪問頻率,同時也帶來了額外的福利就是熱點key的發現。
LFU簡單來講就是用0-255來表示key的訪問頻率,值越大說明訪問頻率越高,并且這里對頻率的計數采用的是基于對數的概率增長,LFU為255可以代表100W次的訪問,關于LFU的實現有興趣的讀者可以參考
https://yq.aliyun.com/articles/278922?spm=a2c4e.11153940.blogcont600648.13.36ab1b52lTEvxh
使用OBJECT FREQ命令即可獲取指定key的訪問頻率,不過需要首先把內存逐出策略設置為allkeys-lfu或者volatile-lfu:
~~~
127.0.0.1:6379> config get maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"
127.0.0.1:6379> object freq counter:000000006889
(error) ERR An LFU maxmemory policy is not selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.
127.0.0.1:6379> config set maxmemory-policy allkeys-lfu
OK
127.0.0.1:6379> object freq counter:000000006889
(integer) 3
~~~
使用scan命令遍歷所有key,再通過OBJECT FREQ獲取訪問頻率并排序,即可得到熱點key。為了方便用戶使用,Redis自帶的客戶端redis-cli也提供了熱點key發現功能,執行redis-cli時加上--hotkeys選項即可,示例如下:
~~~
$./redis-cli --hotkeys
# Scanning the entire keyspace to find hot keys as well as
# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).
[00.00%] Hot key 'counter:000000000002' found so far with counter 87
[00.00%] Hot key 'key:000000000001' found so far with counter 254
[00.00%] Hot key 'mylist' found so far with counter 107
[00.00%] Hot key 'key:000000000000' found so far with counter 254
[45.45%] Hot key 'counter:000000000001' found so far with counter 87
[45.45%] Hot key 'key:000000000002' found so far with counter 254
[45.45%] Hot key 'myset' found so far with counter 64
[45.45%] Hot key 'counter:000000000000' found so far with counter 93
-------- summary -------
Sampled 22 keys in the keyspace!
hot key found with counter: 254 keyname: key:000000000001
hot key found with counter: 254 keyname: key:000000000000
hot key found with counter: 254 keyname: key:000000000002
hot key found with counter: 107 keyname: mylist
hot key found with counter: 93 keyname: counter:000000000000
hot key found with counter: 87 keyname: counter:000000000002
hot key found with counter: 87 keyname: counter:000000000001
hot key found with counter: 64 keyname: myset
~~~
# MEMORY內存分析命令
分析內存可以優化Redis的使用方式,全新的MEMORY命令可以幫助用戶來實現這一操作。
MEMORY命令一共有5個子命令,可以通過MEMORY HELP來查看:
~~~
127.0.0.1:6379> memory help
1) "MEMORY DOCTOR - Outputs memory problems report"
2) "MEMORY USAGE <key> [SAMPLES <count>] - Estimate memory usage of key"
3) "MEMORY STATS - Show memory usage details"
4) "MEMORY PURGE - Ask the allocator to release memory"
5) "MEMORY MALLOC-STATS - Show allocator internal stats"
~~~
關于各個子命令的詳細使用方式可以參考
https://yq.aliyun.com/articles/278910?spm=a2c4e.11153940.blogcont600648.14.36ab1b52lTEvxh
- SQL
- 名詞
- mysql
- 初識mysql
- 備份和恢復
- 存儲引擎
- 數據表損壞和修復
- mysql工具
- 數據庫操作
- 增
- 刪
- 改
- 查
- 數據類型
- 整數類型
- 小數類型
- 日期時間類型
- 字符和文本型
- enum類型
- set類型
- 時間類型
- null與not null和null與空值''的區別
- 數據表操作
- 創建
- 索引
- 約束
- 表選項列表
- 表的其他語句
- 視圖
- sql增刪改查
- sql增
- sql刪
- sql改
- sql查
- sql語句練習
- 連接查詢和更新
- 常用sql語句集錦
- 函數
- 字符函數
- 數值運算符
- 比較運算符與函數
- 日期時間函數
- 信息函數
- 聚合函數
- 加密函數
- null函數
- 用戶權限管理
- 用戶管理
- 權限管理
- pdo
- 與pdo相關的幾個類
- 連接數據庫
- 使用
- pdo的錯誤處理
- pdo結果集對象
- pdo結果集對象常用方法
- pdo預處理
- 常用屬性
- mysql編程
- 事務
- 語句塊
- mysql中的變量
- 存儲函數
- 存儲過程
- 觸發器
- mysql優化
- 存儲引擎
- 字段類型
- 三范式和逆范式
- 索引
- 查詢緩存
- limit分頁優化
- 分區
- 介紹
- 分區算法
- list分區
- range范圍
- Hash哈希
- key鍵值
- 分區管理
- 特別注意
- 分表
- 數據碎片與維護
- innodb表壓縮
- 慢查詢
- explain執行計劃
- count和max,groupby優化
- 子查詢優化
- mysql鎖機制
- 介紹
- 演示
- 總結
- 樂觀鎖和悲觀鎖
- 扛得住的mysql
- 實例和故事
- 系統參數優化
- mysql體系結構
- mysql基準測試
- 索引
- mysql的復制
- win配置MySQL主從
- mysql5.7新特性
- 常見問題
- general log
- 忘記密碼
- uodo log與redo log
- 事務隔離級別
- mysql8密碼登錄
- explain
- 高效的Tree表
- on delete cascade 總結
- mongod
- 簡介
- 集合文檔操作語句
- 增刪改查
- 索引
- 數據導入和導出
- 主從復制
- php7操作mongod
- 權限管理
- redis
- redis簡介
- 3.2版本配置文件
- 3.0版本配置文件
- 2.8版本配置文件
- 配置文件總結
- 外網連接
- 持久化
- RDB備份方式保存數據
- AOF備份方式保存數據
- 總結
- win安裝redis和sentinel部署
- 事務
- Sentinel模式配置
- 分布式鎖
- 管道
- php中redis代碼
- 發布訂閱
- slowlog
- Redis4.0
- scan和keys
- elasticsearch
- 配置說明
- 啟動
- kibana
- kibana下載
- kibana配置文件
- kibana常用功能
- 常用術語
- Beats
- Beats簡介
- Filebeat
- Packetbeat
- Logstash
- 配置
- elasticsearch架構
- es1.7
- head和bigdesk插件
- 插件大全
- 倒排索引
- 單模式下API增刪改查
- mget獲取多個文檔
- 批量操作bulk
- 版本控制
- Mapping映射
- 基本查詢
- Filter過濾
- 組合查詢
- es配置文件
- es集群優化和管理
- logstash
- kibana
- es5.2
- 安裝
- 沖突處理
- 數據備份
- 缺陷不足
- 集群管理api
- 分布式事務
- CAP理論
- BASE模型
- 兩階段提交(2PC)
- TCC (Try-Confirm-Cancle)
- 異步確保型
- 最大努力通知型
- 總結