Redis學習筆記(一):基礎

核心

一、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數據庫的構造很相似,所以二者可以相互轉化。

六、通配符

  1. *:匹配0個或多個字符
  2. ?:匹配1個字符
  3. []:匹配其中一個字符

通用命令

  1. keys [pattern]

    遍歷所以符合條件的key,生產環境不使用

    # 以he開頭的key
    keys he*
    # 以he開頭並且後面跟着a到c字符,此時如果有heb,是可以匹配到的
    keys he[a-c] # 等價於he[abc]
    
  2. dbsize

    計算key的總數

    # mset k1 v1 k2 v2
    dbsize
    # (integer) 2
    sadd myset 1 2 3
    dbsize
    # (integer) 3
    
  3. exists key

  4. del key [key …]

    刪除key,可以刪除多個。

  5. expire key seconds

    設置過期時間(秒)

    EXPIRE hello 60
    #(integer) 1
    ttl hello # -1表示持久化 -2表示不存在 正整數表示剩餘秒數
    #(integer) 57
    
  6. type key

    返回key所對應的值的類型

數據結構和內部編碼

  1. string
    • raw
    • int
    • embstr
  2. hash
    • hashtable
    • ziplist
  3. list
    • linkedlist
    • ziplist
  4. set
    • hashtable
    • intset
  5. zset
    • skiplist
    • ziplist

img

字符串

# 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

  1. 比較難確定,這裏舉個例子:

  2. 命令平均執行時間0.1ms=0.001s。

  3. 業務需要50000QPS。

  4. maxTotal理論值=0.001*50000=50個。實際偏大一些。

  5. 其他因素:

    1. 業務希望Redis併發量
    2. 客戶端執行命令時間
    3. Redis資源:例如nodes(應用個數)* maxTotal是不能超過redis的最大連接數的。(config get maxclients)
    4. 資源開銷:例如雖然希望控制空閒連接,但是不希望因爲連接池的頻繁釋放創建連接造成不必要的開銷。
  6. 建議:

    maxTotal=maxIdle,減少創建新連接的開銷

    建議預熱minIdle–設置爲正數,而不是默認的0,旨在減少第一次啓動後的新連接開銷

線程池問題解決思路

  1. 慢查詢阻塞:池子連接都被hang住
  2. 資源池參數不合理:QPS高,池子小
  3. 連接泄露(沒有close):此類問題比較難定位,例如client list、netstat等,最重要的是代碼
  4. 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章