核心
一、Redis單機多實例原理
每個實例對應不同的配置文件,配置文件對應不同的端口、數據庫文件位置、日誌位置。
二、Redis單實例多數據庫
每個Redis實例都有16個數據庫,下標從0-15,當 set 一個數據時,默認添加到 db0,而select 命令可以切換當前使用的數據庫,例:select 15 Redis
三、數據庫構造
每個數據庫相當於一棵樹的根節點,子節點可以是Redis定義的數據類型之一,也可以是命名空間(namespace),最終子節點(葉子)一定是Redis定義的數據類型之一。
四、Redis命名空間
這個很重要,命名空間用冒號定義,例1:set test1:test2:test3 123 例2:set test1:test5 345 其中 test3和test5 是葉子 命名空間可以擴展樹的深度
五、Redis 與 Json
JavaScript對象 與 Redis數據庫的構造很相似,所以二者可以相互轉化。
六、通配符
*
:匹配0個或多個字符?
:匹配1個字符[]
:匹配其中一個字符
通用命令
-
keys [pattern]
遍歷所以符合條件的key,生產環境不使用
# 以he開頭的key keys he* # 以he開頭並且後面跟着a到c字符,此時如果有heb,是可以匹配到的 keys he[a-c] # 等價於he[abc]
-
dbsize
計算key的總數
# mset k1 v1 k2 v2 dbsize # (integer) 2 sadd myset 1 2 3 dbsize # (integer) 3
-
exists key
-
del key [key …]
刪除key,可以刪除多個。
-
expire key seconds
設置過期時間(秒)
EXPIRE hello 60 #(integer) 1 ttl hello # -1表示持久化 -2表示不存在 正整數表示剩餘秒數 #(integer) 57
-
type key
返回key所對應的值的類型
數據結構和內部編碼
- string
- raw
- int
- embstr
- hash
- hashtable
- ziplist
- list
- linkedlist
- ziplist
- set
- hashtable
- intset
- zset
- skiplist
- ziplist
字符串
# key自增1,如果key不存在,自增後get key 返回1
incr key
# 與incr相反
decr key
# 以步長爲2自增
incrby key 2
# 以步長爲3自增
decrby key 3
# 不管key是否存在都設置
set key value
# setIfAbsent
setnx key value
# setIfPresent
set key value xx
# 同時獲取多個key值
mget k1 k2 k3 k4
# 同時set多個k-v對
mset k1 v1 k2 v2
# 原子操作,輸入新值返回舊值
getset key newValue
# 追加
append key subfix
# 返回字符串長度,需要注意中文
strlen key
# 增加key對應的值3.5
incrbyfloat key 3.5
# 獲取字符串指定下標所有的值
getrange key start_index end_index
# 設置指定下標所有對應的值
setrange key index value
哈希
實際上就是一個Map<String, Map<String, Object>>
# hset key field value
# 爲user設置年齡&名字屬性值
hset user:1 age 33
hset user:1 name yan
hget user:1 age
hget user:1 name
hdel user:1 age
hexists user:1 age
hlen user:1 name
hmset user:2 age 33 name yan
hmget user:2 age name
列表
- 有序
- 可重複
- 兩邊彈出
# 右邊PUSH
RPUSH key v1 v2 v3
# 左邊彈出
LPOP key
LPUSH key v0
LPOP key
# case count
# >0: 從左到右,刪除最多count個和value相等的元素
# <0: 從右向左,刪除最多Math.abs(count)個和value相等的元素
# =0: 刪除所有和value相等的元素
LREM key count v2
# 獲取子列表,下標範圍[2-3]
LRANGE key 2 3
# 在list指定的值前/後插入newValue
linsert key [before|after] value newValue
# 按照索引範圍修剪列表,保留索引範圍[start-end]的元素
ltrim key start end
# 設置列表指定索引處值
lset key index newValue
# 阻塞左|右彈出 timeout爲阻塞超時時間
blpop key timeout
brpop key timeout
集合
無序不重複
# 向集合key添加元素,如果存在則失敗
sadd key element
# 將集合key中的元素移除
srem key element
# 展示所有元素,無序,小心使用
smembers key
# 從集合中彈出count個元素
spop key [count]
# 隨機展示count個元素,不會破壞集合
srandmember key [count]
# 差集
sdiff k1 k2
# 交集
sinter k1 k2
# 並集
sunion k1 k2
有序集合
# 添加score和element,按照score從小到大
zadd key score element...
zrem key element...
# 獲取key-ele所對應的分值
zscore key element
# 返回指定索引範圍內的升序元素,索引指的是排名
# 這裏的索引可以是負數
zrange key start end [withscores]
# 返回
zrangebyscore key min max [withscores]
# 統計在某個分值範圍內的元素數量
zcount key minScore maxScore
# 根據排名批量刪除
zremrangebyrank key start stop
# 根據分值批量刪除
zremrangebyrank key start stop
Jedis
Jedis是java客戶端的基礎實現。
依賴
gradle:
compile group: 'redis.clients', name: 'jedis', version: '3.2.0'
maven:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
實現
Jedis jedis = new Jedis("127.0.0.1");
//jedis = new Jedis("127.0.0.1", 6379);
//jedis = new Jedis("127.0.0.1", 6379, 100);
JedisPool
使用CommonsPool爲底層,降低頻繁單點連接造成的資源消耗。
依賴與Jedis相同。
JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
// 自定義連接參數
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig<>();
// poolConfig.set...
poolConfig.setMaxIdle(20);
poolConfig.setMaxTotal(20);
poolConfig.setMinIdle(1);
poolConfig.setMaxWaitMillis(10000);
// 新建連接池
JedisPool jedisPool = new JedisPool(poolConfig, InetAddress.getLocalHost().getHostAddress());
Jedis resource = jedisPool.getResource();
// opt
System.out.println(resource.ping());
// 歸還連接池
resource.close();
方案對比
優點 | 缺點 | |
---|---|---|
直連 | 1. 簡單方便 2. 適用於少量長期連接的場景 |
1. 存在每次新建、關閉TCP開銷 2. 資源無法控制,存在連接泄露的可能 3. Jedis對象線程不安全 |
連接池 | 1. Jedis預先生成,降低開銷使用 2. 連接池的形式保護和控制資源的使用 |
相對麻煩,尤其是在資源的管理上需要很多參數來保證,一旦規劃不當也會出現問題。 |
由此可見,對於連接池的配置尤其重要。
資源池(commons-pool)參數
參數名 | 含義 | 默認值 | 使用建議 |
---|---|---|---|
maxTotal | 資源池最大連接數 | 8 | |
maxIdle | 資源池允許最大空閒連接數 | 8 | |
minIdle | 資源池確保最少空閒連接數 | 0 | 建議設爲正數,提前預熱 |
jmxEnabled | 是否開啓jmx監控,用於監控 | true | 開啓 |
blockWhenExhausted | 當資源池用盡後,調用者是否要等待。只有當爲true時,下面的maxWaitMillis纔會生效 | true | 使用默認值 |
maxWaitMillis | 當資源池連接用盡後,調用者的最大等待時間(毫秒) | -1:永久不超時 | 不建議使用默認值 |
testOnBorrow | 向資源池借用連接時,是否做連接有效性檢測(ping),無效連接會被移除 | false | false |
testOnReturn | 向資源池歸還連接時,是否做連接有效性檢測(ping),無效連接會被移除 | false | false |
maxTotal
-
比較難確定,這裏舉個例子:
-
命令平均執行時間0.1ms=0.001s。
-
業務需要50000QPS。
-
maxTotal理論值=0.001*50000=50個。實際偏大一些。
-
其他因素:
- 業務希望Redis併發量
- 客戶端執行命令時間
- Redis資源:例如nodes(應用個數)* maxTotal是不能超過redis的最大連接數的。(config get maxclients)
- 資源開銷:例如雖然希望控制空閒連接,但是不希望因爲連接池的頻繁釋放創建連接造成不必要的開銷。
-
建議:
maxTotal=maxIdle,減少創建新連接的開銷
建議預熱minIdle–設置爲正數,而不是默認的0,旨在減少第一次啓動後的新連接開銷
線程池問題解決思路
- 慢查詢阻塞:池子連接都被hang住
- 資源池參數不合理:QPS高,池子小
- 連接泄露(沒有close):此類問題比較難定位,例如client list、netstat等,最重要的是代碼
- DNS異常等。
模擬
public static void main(String[] args) {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(1);
jedisPoolConfig.setMaxTotal(1);
// 設置獲取資源最大等待時間
jedisPoolConfig.setMaxWaitMillis(1000);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, "localhost");
jedisPool.getResource();
jedisPool.getResource();
}
由於第一次獲取資源後沒有調用Jedis::close
,即未將資源歸還連接池,第二次獲取時,由於不滿足連接池配置條件(1.最大連接數;2.最大等待100ms)而拋出異常
Exception in thread "main" redis.clients.jedis.exceptions.JedisExhaustedPoolException: Could not get a resource since the pool is exhausted
at redis.clients.jedis.util.Pool.getResource(Pool.java:53)
at redis.clients.jedis.JedisPool.getResource(JedisPool.java:234)
at Main.main(Main.java:14)
Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:439)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:349)
at redis.clients.jedis.util.Pool.getResource(Pool.java:50)
... 2 more