天天看點

Java 應用:關于Redis入門的詳細解析

作者:晾幹的紅領巾

一、簡介

1.1 Redis是什麼

Redis是一個基于記憶體的開源鍵值存儲系統,它可以用作資料庫、緩存、消息中間件和任務隊列等多種用途。

1.2 Redis的優點和缺點

Redis的優點包括:

  1. 非常快速:Redis是一個基于記憶體的存儲系統,是以它的讀寫速度非常快。
  2. 支援多種資料結構:Redis支援多種資料結構,包括字元串、清單、集合、哈希表和有序集合等,是以可以适用于不同的場景。
  3. 支援事務:Redis的事務機制可以讓多個指令在一個事務中執行,保證操作的原子性和一緻性。
  4. 支援持久化:Redis可以将資料持久化到磁盤上,以保證資料的可靠性和持久性。
  5. 支援複制和高可用性:Redis支援主從複制和哨兵機制,可以實作高可用性和負載均衡。

Redis的缺點包括:

  1. 記憶體限制:Redis的資料存儲在記憶體中,是以受到記憶體大小的限制,不能存儲過大的資料。
  2. 資料持久化成本較高:Redis的持久化機制會帶來一定的性能損失和存儲成本,需要在性能和資料安全之間做出平衡。
  3. 不支援複雜查詢:Redis不支援複雜的查詢操作,例如關系型資料庫中的join操作。
  4. 不能作為全局鎖:Redis的鎖機制隻能作用于單個Redis執行個體,不能作為全局鎖使用。

二、在Java中使用Redis

2.1 Redis的Java用戶端

要在Java中使用Redis,需要借助Redis的Java用戶端。Redis官方提供了一些Java用戶端,比如Jedis和Lettuce等,這些用戶端都是基于Redis協定實作的,可以通過Java語言來通路和操作Redis資料庫。

Jedis是Redis官方推薦的Java用戶端,使用簡單,性能高效,而且支援比較完整的Redis指令。在使用Jedis時,需要在項目中引入jedis依賴,比如Maven項目中可以在pom.xml檔案中添加如下依賴:

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>           

引入依賴後,可以通過以下方式建立一個Jedis用戶端連接配接:

Jedis jedis = new Jedis("localhost", 6379);           

其中,"localhost"是Redis伺服器的位址,6379是Redis伺服器的端口号。如果Redis伺服器啟用了密碼驗證,還需要在建立連接配接時進行認證:

Jedis jedis = new Jedis("localhost", 6379);
jedis.auth("password");           

建立連接配接之後,就可以使用Jedis提供的API來進行Redis操作了。比如,可以通過以下代碼向Redis中添加一個字元串類型的鍵值對:

jedis.set("key", "value");           

除了Jedis之外,還有其他的Java用戶端可以用于通路Redis,比如Lettuce、Redisson等,它們都提供了比較完善的Redis操作API,可以根據自己的需要選擇使用。

2.2 Redis的連接配接和配置

連接配接Redis需要指定Redis伺服器的IP位址和端口号。在Jedis中,可以使用JedisPool來管理連接配接池,可以在應用程式初始化時建立一個JedisPool對象,然後在需要使用Redis時從連接配接池中擷取一個Jedis對象進行操作。

以下是一個Jedis連接配接池的配置示例:

JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(20);
poolConfig.setMaxIdle(10);
poolConfig.setMinIdle(5);
poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnReturn(true);
String redisHost = "127.0.0.1";
int redisPort = 6379;
int redisTimeout = 2000;
JedisPool jedisPool = new JedisPool(poolConfig, redisHost, redisPort, redisTimeout);           

在這個示例中,我們使用JedisPoolConfig來配置連接配接池的參數,包括連接配接池的最大連接配接數、最大空閑連接配接數、最小空閑連接配接數等。然後,我們指定Redis伺服器的IP位址和端口号,并設定連接配接的逾時時間為2秒。

在建立JedisPool對象時,可以配置一些參數來控制連接配接池的行為,以下是常用的配置項:

  1. 最大連接配接數(maxTotal) 最大連接配接數指連接配接池中最多可以存在的連接配接數量。如果超過這個數量,新的連接配接請求會被阻塞,直到有連接配接被釋放回連接配接池中。預設值為8。
  2. 最大空閑連接配接數(maxIdle) 最大空閑連接配接數指連接配接池中最多可以存在的空閑連接配接數量。當連接配接池中的連接配接數量超過這個數量時,多餘的連接配接會被釋放。預設值為8。
  3. 最小空閑連接配接數(minIdle) 最小空閑連接配接數指連接配接池中最少需要保持的空閑連接配接數量。當連接配接池中的連接配接數量低于這個數量時,會自動建立新的連接配接。預設值為0。
  4. 連接配接逾時時間(timeout) 連接配接逾時時間指連接配接Redis伺服器的逾時時間。如果連接配接時間超過這個時間,連接配接會被認為是失敗的。預設值為2000毫秒。
  5. 阻塞等待連接配接的逾時時間(maxWaitMillis) 阻塞等待連接配接的逾時時間指當連接配接池中的連接配接數量已經達到最大連接配接數時,新的連接配接請求會被阻塞的時間。如果阻塞等待的時間超過這個值,連接配接請求會被認為是失敗的。預設值為-1,表示無限等待。
  6. 是否進行連接配接測試(testOnBorrow) 是否進行連接配接測試指在從連接配接池中擷取連接配接時,是否進行連接配接測試。如果開啟這個選項,每次擷取連接配接時都會嘗試連接配接Redis伺服器,并檢查連接配接是否可用。預設值為false。
  7. 是否在歸還連接配接時測試連接配接(testOnReturn) 是否在歸還連接配接時測試連接配接指在将連接配接歸還到連接配接池中時,是否進行連接配接測試。如果開啟這個選項,每次将連接配接歸還到連接配接池時都會嘗試連接配接Redis伺服器,并檢查連接配接是否可用。預設值為false。

除了上述配置項外,JedisPool還提供了其他的配置項,比如連接配接空閑時是否進行心跳檢測等。根據實際需要進行配置即可。

2.3 Redis的基本操作:增删改查

存儲資料

// 存儲字元串
jedis.set("key", "value");
// 存儲Hash
Map<String, String> map = new HashMap<>();
map.put("field1", "value1");
map.put("field2", "value2");
jedis.hmset("hashkey", map);
// 存儲List
jedis.rpush("listkey", "value1", "value2", "value3");
// 存儲Set
jedis.sadd("setkey", "value1", "value2", "value3");
// 存儲Sorted Set
jedis.zadd("zsetkey", 1, "value1");
jedis.zadd("zsetkey", 2, "value2");
jedis.zadd("zsetkey", 3, "value3");           

擷取資料

// 擷取字元串
String value = jedis.get("key");
// 擷取Hash
Map<String, String> map = jedis.hgetAll("hashkey");
// 擷取List
List<String> list = jedis.lrange("listkey", 0, -1);
// 擷取Set
Set<String> set = jedis.smembers("setkey");
// 擷取Sorted Set
Set<String> zset = jedis.zrange("zsetkey", 0, -1);           

更新資料

// 更新字元串
jedis.set("key", "newvalue");
// 更新Hash
jedis.hset("hashkey", "field1", "newvalue1");
// 更新List
jedis.lset("listkey", 1, "newvalue2");
// 更新Set
jedis.srem("setkey", "value1");
jedis.sadd("setkey", "newvalue1");
// 更新Sorted Set
jedis.zrem("zsetkey", "value1");
jedis.zadd("zsetkey", 4, "newvalue1");           

删除資料

// 删除字元串
jedis.del("key");
// 删除Hash
jedis.hdel("hashkey", "field1");
// 删除List
jedis.lrem("listkey", 1, "value2");
// 删除Set
jedis.srem("setkey", "value1");
// 删除Sorted Set
jedis.zrem("zsetkey", "value1");           

以上就是Java中使用Redis進行增删改查的基本操作。

三、Redis支援的資料結構

3.1 字元串(string)

字元串是Redis中最常用的資料結構之一,可以用于存儲使用者資訊、緩存資料、計數器等。在使用字元串時,需要注意避免超過最大長度的限制,同時要考慮資料類型轉換的問題。

下面是字元串常用的操作:

  1. SET key value:設定鍵值對。
  2. GET key:擷取鍵對應的值。
  3. GETRANGE key start end:擷取字元串指定範圍内的子串。
  4. SETRANGE key offset value:在指定偏移量處修改字元串。
  5. APPEND key value:在字元串末尾追加字元串。
  6. STRLEN key:擷取字元串的長度。
  7. INCR key:将鍵的值自增1。
  8. DECR key:将鍵的值自減1。
  9. INCRBY key increment:将鍵的值增加指定的整數值。
  10. DECRBY key decrement:将鍵的值減少指定的整數值。
  11. SETEX key seconds value:設定鍵值對,并指定過期時間(機關為秒)。
  12. PSETEX key milliseconds value:設定鍵值對,并指定過期時間(機關為毫秒)。
  13. MSET key1 value1 key2 value2 ... keyN valueN:批量設定多個鍵值對。
  14. MGET key1 key2 ... keyN:批量擷取多個鍵對應的值。
  15. SETNX key value:僅在鍵不存在時設定鍵的值。
  16. GETSET key value:設定鍵的值,并傳回舊的值。

3.2 清單(list)

清單(list)可以存儲多個有序的字元串元素,支援在清單頭部和尾部進行添加和删除操作。清單可以當做隊列(先進先出)或者棧(後進先出)來使用。

下面是清單常用的操作:

  1. LPUSH key value1 [value2]:在清單的頭部添加一個或多個元素。
  2. RPUSH key value1 [value2]:在清單的尾部添加一個或多個元素。
  3. LPOP key:移除并傳回清單的頭部元素。
  4. RPOP key:移除并傳回清單的尾部元素。
  5. LINDEX key index:擷取指定索引位置的元素。
  6. LLEN key:擷取清單的長度。
  7. LRANGE key start stop:擷取清單中指定範圍内的元素。
  8. LREM key count value:從清單中移除指定個數的元素。
  9. LTRIM key start stop:保留指定範圍内的元素,移除其它元素。
  10. BLPOP key1 [key2] timeout:阻塞式地移除并傳回清單的頭部元素。
  11. BRPOP key1 [key2] timeout:阻塞式地移除并傳回清單的尾部元素。
  12. BRPOPLPUSH source destination timeout:阻塞式地将一個清單的尾部元素移動到另一個清單的頭部。
  13. LINSERT key BEFORE|AFTER pivot value:在清單中查找指定元素,并在其前或後插入新元素。

3.3 集合(set)

集合(set)是一種無序資料結構,它包含一些唯一的、無序的字元串元素。集合中不允許有重複的元素。集合支援并集、交集、差集等操作。

下面是集合常用的操作:

  1. SADD key member1 [member2]:向集合添加一個或多個元素。
  2. SREM key member1 [member2]:從集合中移除一個或多個元素。
  3. SISMEMBER key member:判斷元素是否在集合中。
  4. SMEMBERS key:擷取集合中所有的元素。
  5. SUNION key1 [key2]:計算多個集合的并集。
  6. SINTER key1 [key2]:計算多個集合的交集。
  7. SDIFF key1 [key2]:計算多個集合的差集。
  8. SMOVE source destination member:将元素從一個集合移動到另一個集合。
  9. SCARD key:擷取集合的元素數量。
  10. SRANDMEMBER key [count]:随機擷取集合中的一個或多個元素。

3.4 哈希表(hash)

哈希表(hash)是一個string類型的field和value的映射表,也就是鍵值對的集合。哈希表通常被用于表示對象,每個哈希表都可以存儲多個鍵值對。

下面是哈希表常用的操作:

  1. HSET key field value:向哈希表添加一個鍵值對。
  2. HGET key field:擷取哈希表中指定field的value值。
  3. HMSET key field1 value1 [field2 value2]:向哈希表添加多個鍵值對。
  4. HMGET key field1 [field2]:擷取哈希表中多個field的value值。
  5. HDEL key field1 [field2]:從哈希表中删除一個或多個field。
  6. HEXISTS key field:判斷哈希表中是否存在指定field。
  7. HKEYS key:擷取哈希表中所有的field。
  8. HVALS key:擷取哈希表中所有的value。
  9. HLEN key:擷取哈希表中鍵值對的數量。
  10. HINCRBY key field increment:為哈希表中指定field的value增加一個整數。

哈希表可以用于存儲對象的屬性和屬性值,比如使用者對象的姓名、年齡、位址等資訊。使用哈希表時需要注意,當哈希表中的鍵值對較多時,會對Redis的記憶體消耗産生較大的影響,是以需要謹慎使用。

3.5 有序集合(sorted set)

有序集合(Sorted Set)是一種鍵值對的有序集合,其中的每個元素都與一個分數相關聯。有序集合通常被用于需要排序的場景,比如排行榜、時間線等。

下面是有序集合常用的操作:

  1. ZADD key score1 member1 [score2 member2]:向有序集合添加一個或多個元素。
  2. ZREM key member1 [member2]:從有序集合中移除一個或多個元素。
  3. ZSCORE key member:擷取有序集合中指定元素的分數。
  4. ZRANK key member:擷取有序集合中指定元素的排名,排名從0開始。
  5. ZRANGE key start stop [WITHSCORES]:擷取有序集合中指定排名範圍内的元素,可以通過WITHSCORES選項擷取元素的分數。
  6. ZREVRANGE key start stop [WITHSCORES]:擷取有序集合中指定排名範圍内的元素,按照分數從大到小排序。
  7. ZCOUNT key min max:擷取有序集合中指定分數範圍内的元素數量。
  8. ZINCRBY key increment member:為有序集合中指定元素的分數增加一個數值。
  9. ZCARD key:擷取有序集合中元素的數量。
  10. ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]:擷取有序集合中指定分數範圍内的元素,可以通過WITHSCORES選項擷取元素的分數,可以通過LIMIT選項控制傳回結果的數量。

有序集合是一種非常有用的資料結構,可以用于排行榜、計數器、票選等場景。在使用有序集合時,需要注意避免在資料量較大時對性能的影響。如果需要對元素進行增删改查等頻繁的操作,可以考慮使用Hash表。

3.6 Bitmap

Bitmap可以将一個二進制的位圖儲存在Redis的字元串值中,用于對某些狀态進行标記。例如,可以用Bitmap記錄使用者的簽到情況,或者統計某個網站每天的通路量等。

Bitmap支援的操作包括:

  1. SETBIT key offset value:将key對應的位圖中offset位的值設為value(0或1),如果offset超過了位圖的長度,則自動進行擴充。
  2. GETBIT key offset:擷取key對應的位圖中offset位的值,傳回0或1。
  3. BITCOUNT key [start end]:統計key對應的位圖中,從start位到end位中值為1的位的數量,如果沒有指定start和end,則統計整個位圖中值為1的位的數量。
  4. BITOP operation destkey key1 [key2...]: 對多個位圖進行邏輯操作,支援AND、OR、XOR、NOT四種操作。結果儲存在destkey對應的位圖中。

Bitmap可以用于對大量的布爾狀态進行快速的儲存和計算。在使用Bitmap時,需要注意位圖的長度,以及對位圖進行操作的正确性。例如,如果某個位圖的長度較長,會占用較多的記憶體空間。同時,因為Redis中的資料都是單線程處理的,對較大的位圖進行複雜的位運算可能會影響Redis的性能。

3.7 HyperLogLog

HyperLogLog可以用于對大資料流中的不重複元素進行近似計數。HyperLogLog的原理是,通過将元素進行哈希,并将哈希值中的一部分作為辨別,将元素分散到不同的桶中,然後對每個桶中的元素進行計數,再對計數結果進行合并。由于哈希的不可逆性,可以保證在較高的機率下,同一個元素不會被分到多個桶中。

HyperLogLog支援的操作包括:

  1. PFADD key element [element ...]:向HyperLogLog中添加一個或多個元素。
  2. PFCOUNT key [key ...]:統計HyperLogLog中不同元素的數量。
  3. PFMERGE destkey sourcekey [sourcekey ...]:将多個HyperLogLog合并為一個HyperLogLog。

HyperLogLog的計數結果是近似值,并不是精确值,但是其計算結果的誤差很小,在實際應用中是可接受的。HyperLogLog可以用于對通路網站的IP位址、某個産品的浏覽量等大量不重複元素的統計。在使用HyperLogLog時,需要注意調整哈希函數的位數和桶的數量,以及對誤差的容忍度。

3.8 地理空間索引(Geospatial)

Geospatial可以用于地理空間索引和查詢。Geospatial可以将地理位置(經度和緯度)作為元素儲存在Redis中,并支援基于距離的查詢和範圍查詢。

Geospatial支援的操作包括:

  1. GEOADD key longitude latitude member [longitude latitude member ...]:向key對應的地理空間中添加一個或多個元素。
  2. GEOPOS key member [member ...]:擷取key對應的地理空間中一個或多個元素的經緯度坐标。
  3. GEODIST key member1 member2 [unit]:擷取key對應的地理空間中兩個元素之間的距離,可以指定傳回值的機關(預設為米)。
  4. GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]:根據給定的經緯度和半徑,在key對應的地理空間中查找符合條件的元素,可以指定傳回值的方式和數量。
  5. GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]:根據給定的元素和半徑,在key對應的地理空間中查找符合條件的元素,可以指定傳回值的方式和數量。

Geospatial可以用于地圖服務、周邊搜尋等場景。在使用Geospatial時,需要注意使用合适的經緯度坐标系和距離機關,以及對查詢結果的過濾和排序。

四、Redis的持久化方式

4.1 RDB方式

RDB是什麼

RDB(Redis DataBase)是Redis的一種持久化方式,它可以在指定的時間間隔内将Redis的資料集快照儲存到硬碟上。RDB持久化方式通過生成一個二進制的RDB檔案來實作資料的持久化。

RDB方式的優點是:

  1. RDB檔案是一個二進制檔案,可以将其複制到其他地方,以備份和恢複資料。
  2. RDB方式可以在Redis重新開機時快速地加載大量資料。
  3. RDB方式比AOF(Append Only File)方式占用更少的磁盤空間。

RDB方式的缺點是:

  1. RDB檔案隻儲存Redis在指定時間間隔内的資料集狀态,如果Redis意外停機,将會導緻資料的丢失。
  2. RDB檔案的建立頻率和Redis資料集的大小成正比。
  3. RDB檔案的建立會導緻Redis在一段時間内停止接收用戶端的請求,因為Redis在建立RDB檔案時會阻塞所有操作。

如何進行配置

配置RDB方式的參數可以在Redis的配置檔案redis.conf中進行。以下是一些常用的配置項:

save 900 1 # 在900秒内,如果有至少一個鍵被改動,則儲存資料到RDB檔案
save 300 10 # 在300秒内,如果有至少10個鍵被改動,則儲存資料到RDB檔案
save 60 10000 # 在60秒内,如果有至少10000個鍵被改動,則儲存資料到RDB檔案
dbfilename dump.rdb # RDB檔案的檔案名
dir /var/lib/redis # RDB檔案的儲存路徑           

在以上配置中,save指定了RDB檔案的儲存政策,dbfilename指定了RDB檔案的檔案名,dir指定了RDB檔案的儲存路徑。可以根據實際情況調整這些參數,以達到更好的性能和可靠性。同時,在使用RDB方式時,也可以通過手動執行SAVE指令,來生成一個RDB檔案。

4.2 AOF方式

AOF是什麼

AOF(Append Only File)是Redis的一種持久化方式,它會将每個寫指令追加到一個檔案中,這樣就可以在Redis重新開機時重新執行這些寫指令,進而恢複資料集的狀态。

AOF方式的優點是:

  1. AOF檔案是一個文本檔案,可以友善地進行人工編輯,進而恢複資料。
  2. AOF方式比RDB方式更可靠,因為它可以在Redis停機時自動恢複資料。
  3. AOF方式可以保證Redis在每次寫操作時都會同步到磁盤上,進而減少資料丢失的風險。
  4. AOF方式的缺點是:
  5. AOF檔案的大小通常比RDB檔案大,因為它記錄了所有的寫操作。
  6. AOF檔案的恢複速度比RDB方式慢,因為需要重新執行所有的寫操作。
  7. AOF檔案的寫入會降低Redis的性能,因為需要頻繁地将資料寫入磁盤。

如何進行配置

配置AOF方式的參數可以在Redis的配置檔案redis.conf中進行。以下是一些常用的配置項:

appendonly yes # 啟用AOF持久化方式
appendfilename "appendonly.aof" # AOF檔案的檔案名
appendfsync everysec # 每秒将AOF緩沖區寫入磁盤
dir /var/lib/redis # AOF檔案的儲存路徑           

在以上配置中,appendonly指定了啟用AOF方式,appendfilename指定了AOF檔案的檔案名,appendfsync指定了将AOF緩沖區寫入磁盤的政策,dir指定了AOF檔案的儲存路徑。可以根據實際情況調整這些參數,以達到更好的性能和可靠性。同時,在使用AOF方式時,也可以通過手動執行BGREWRITEAOF指令,來重新生成一個AOF檔案。

五、Redis的并發和鎖的實作

5.1 Redis的事務機制

Redis支援事務機制,事務可以将多個指令封裝在一起,然後一次性執行,進而保證多個指令的原子性。Redis的事務機制采用的是樂觀鎖的機制,具體實作方式是:

  1. 事務開始時,Redis會将用戶端的狀态設定為MULTI。
  2. 在MULTI狀态下,用戶端可以執行多個指令。
  3. 執行完所有指令後,用戶端可以調用EXEC指令,Redis會在執行之前檢查所有指令是否可以順利執行。
  4. 如果所有指令都可以順利執行,則Redis會将它們一起執行,進而保證原子性。
  5. 如果其中有一個指令不能執行,則Redis會復原所有已執行的指令,進而保證資料的一緻性。

事務的具體實作方式可以通過Redis的MULTI、EXEC、DISCARD和WATCH指令來完成。其中,MULTI和EXEC是事務的核心指令,DISCARD可以用來取消一個事務,而WATCH可以用來實作Redis的樂觀鎖機制。

在使用事務時,需要注意以下幾點:

  1. 事務期間的所有指令都不會被立即執行,而是緩存在一個隊列中,直到EXEC指令被調用。
  2. 事務期間的所有指令都必須是無狀态的,即不能依賴于之前的狀态,否則會出現意料之外的結果。
  3. 事務期間可以使用Redis的流水線技術來提高性能,即在MULTI和EXEC之間,可以一次性發送多個指令給Redis,進而減少通信的開銷。

事務是Redis實作并發和鎖機制的一種方式,它可以保證多個指令的原子性,但是不能解決所有的并發問題。在實際使用中,還需要根據具體的業務場景,使用Redis提供的其他并發和鎖機制來保證資料的一緻性和可靠性。

5.2 Redis的樂觀鎖和悲觀鎖

樂觀鎖

Redis的樂觀鎖是通過WATCH指令和CAS(Compare And Set)指令實作的。WATCH指令可以監視一個或多個鍵的變化情況,當任意一個監視的鍵被修改時,後續的事務會被取消。CAS指令可以在執行SET指令之前,檢查鍵的值是否符合預期,如果符合預期,則執行SET指令,否則不執行。

使用樂觀鎖時,需要注意以下幾點:

  • WATCH指令隻能用于事務中,而CAS指令可以單獨使用。
  • WATCH指令可以監視多個鍵的變化情況,但是監視的鍵數量不能太多,否則會降低性能。
  • CAS指令需要預先擷取鍵的值,并在執行SET指令之前進行檢查,這可能會引起競争條件和死鎖問題。

悲觀鎖

Redis的悲觀鎖是通過SETNX(SET if Not eXists)指令實作的。SETNX指令可以在鍵不存在時,執行SET指令,并将鍵的值設定為指定的值,進而實作悲觀鎖的效果。

使用悲觀鎖時,需要注意以下幾點:

  • SETNX指令隻能用于單個鍵,而不能用于多個鍵或事務中。
  • SETNX指令可以保證并發通路的正确性,但是可能會引起性能問題和死鎖問題。
  • SETNX指令不能設定過期時間,是以可能會引起資料存儲的問題。

六、總結

通過本文的介紹,我們可以清晰地了解Redis的基本概念、使用方法和注意事項。

繼續閱讀