Redis的數(shù)據(jù)類型String 數(shù)據(jù)結(jié)構(gòu)和常用命令hash 數(shù)據(jù)結(jié)構(gòu)和常用命令List 數(shù)據(jù)結(jié)構(gòu)和常用命令Set 數(shù)據(jù)結(jié)構(gòu)和常用命令Zset 數(shù)據(jù)結(jié)構(gòu)和常用命令Redis HyperLogLog常用命令
Redis的數(shù)據(jù)類型
Redis 是一種基于內(nèi)存的數(shù)據(jù)庫(kù)陌选,并且提供一定的持久化功能替蔬,它是一種鍵值數(shù)據(jù)庫(kù)厨幻,使用 key 作為索引找到當(dāng)前緩存的數(shù)據(jù)相嵌,并且返回給程序調(diào)用者。
當(dāng)前的 Redis 支持 5 種基礎(chǔ)數(shù)據(jù)類型和 3 種特殊數(shù)據(jù)類型况脆,它們分別是字符串(String)饭宾、哈希結(jié)構(gòu)(hash)、列表(List)格了、集合(set)看铆、有序集合(zset)和基數(shù)(HyperLogLog)、GEO(geospatial)笆搓、位圖(bitmap)性湿。
Redis 定義的這數(shù)據(jù)類型是十分有用的,它除了提供簡(jiǎn)單的存儲(chǔ)功能满败,還能對(duì)存儲(chǔ)的數(shù)據(jù)進(jìn)行一些計(jì)算肤频。
比如字符串可以支持浮點(diǎn)數(shù)的自增、自減算墨、字符求子串宵荒,集合求交集、并集,有序集合進(jìn)行排序等报咳,所以使用它們有利于對(duì)一些不太大的數(shù)據(jù)集合進(jìn)行快速計(jì)算侠讯,簡(jiǎn)化編程,同時(shí)它也比數(shù)據(jù)庫(kù)要快得多暑刃,所以它們對(duì)系統(tǒng)性能的提升十分有意義厢漩。
-
STRING(字符串):可以是保存字符串、整數(shù)和浮點(diǎn)數(shù) 岩臣,可以對(duì)字符串進(jìn)行操作
比如增加字符或者求子串:
如果是整數(shù)或者浮點(diǎn)數(shù)溜嗜,可以實(shí)現(xiàn)計(jì)算,比如自增等
-
LIST(列表):它是一個(gè)鏈表架谎,它的每一個(gè)節(jié)點(diǎn)都包含一個(gè)字符串
Redis 支持從鏈表的兩端插入或者彈出節(jié)點(diǎn)炸宵,或者通過(guò)偏移對(duì)它進(jìn)行裁剪;
還可以讀取一個(gè)或者多個(gè)節(jié)點(diǎn)谷扣,根據(jù)條件刪除或者查找節(jié)點(diǎn)等
-
SET(集合):它是一個(gè)收集器土全,但是是無(wú)序的,在它里而每一個(gè)元素都是一個(gè)字符串会涎,而且是獨(dú)一無(wú)二裹匙,各不相同的
可以新增、讀取在塔、刪除單個(gè)元素:檢測(cè)一個(gè)元素是否在集合中幻件;
計(jì)算它和其他集合的交集、并集和差集等蛔溃;
隨機(jī)從集合中讀取元素
-
HASH(哈希散列表):它類似于 Java 語(yǔ)言中的 Map绰沥,是一個(gè)鍵值對(duì)應(yīng)的無(wú)序列表
- 可以増、刪贺待、査徽曲、改單個(gè)鍵值對(duì),也可以獲取所有的鍵值對(duì)
-
ZSET(有序集合):它是一個(gè)有序的集合麸塞,可以包含字符 串秃臣、整數(shù)、浮點(diǎn)數(shù)哪工、分值(score)奥此,元素 的排序是依據(jù)分值的大小來(lái)決定的
- 可以增、刪雁比、査稚虎、改元素,根據(jù)分值的范圍或者成員 來(lái)獲取對(duì)應(yīng)的元索
-
geospatial(Geo):Redis 在 3.2 推出 Geo 類型偎捎,該功能可以推算出地理位置信息蠢终,兩地之間的距離序攘。
規(guī)則:兩極無(wú)法直接添加,一般會(huì)下載城市數(shù)據(jù)寻拂,直接通過(guò) Java 程序一次性導(dǎo)入程奠。
有效的經(jīng)度從 -180 度到 180 度。有效的緯度從 -85.05112878 度到 85.05112878 度祭钉。當(dāng)坐標(biāo)位置超出指定范圍時(shí)瞄沙,該命令將會(huì)返回一個(gè)錯(cuò)誤。
-
HyperLogLog(基數(shù)):它的作用是計(jì)算重復(fù)的值慌核,以確定存儲(chǔ)的數(shù)量
- 只提供基數(shù)的運(yùn)算帕识,不提供返回的功能
-
bitmap (位圖):是通過(guò)最小的單位bit來(lái)進(jìn)行0或者1的設(shè)置,表示某個(gè)元素對(duì)應(yīng)的值或者狀態(tài)遂铡。一個(gè)bit的值,或者是0晶姊,或者是1扒接;也就是說(shuō)一個(gè)bit能存儲(chǔ)的最多信息是2。
- bitmap 常用于統(tǒng)計(jì)用戶信息比如活躍粉絲和不活躍粉絲们衙、登錄和未登錄钾怔、是否打卡等。
String 數(shù)據(jù)結(jié)構(gòu)和常用命令
字符串(String)是 Redis 最基本的數(shù)據(jù)結(jié)構(gòu)蒙挑,它將以一個(gè)鍵和一個(gè)值存儲(chǔ)于 Redis 內(nèi)部宗侦,它猶如 Java 的 Map 結(jié)構(gòu),讓 Redis 通過(guò)鍵去找到值忆蚀。Redis 字符串的數(shù)據(jù)結(jié)構(gòu)如圖 1 所示矾利。
圖 1 Redis 字符串?dāng)?shù)據(jù)結(jié)構(gòu)
Redis 會(huì)通過(guò) key 去找到對(duì)應(yīng)的字符串,比如通過(guò) key1 找到 value1馋袜,又如在 Java 互聯(lián)網(wǎng)中男旗,假設(shè)產(chǎn)品的編號(hào)為 0001,只要設(shè)置 key 為 product_0001欣鳖,就可以通過(guò) product_0001 去保存該產(chǎn)品到 Redis 中察皇,也可以通過(guò) product_0001 從 redis 中找到產(chǎn)品信息。
常用命令如下:
命 令 | 說(shuō) 明 | 備 注 |
---|---|---|
set key value | 設(shè)置鍵值對(duì) | 最常用的寫入命令 |
get key | 通過(guò)鍵獲取值 | 最常用的讀取命令 |
del key | 通過(guò) key泽台,刪除鍵值對(duì) | 刪除命令什荣,返冋刪除數(shù),注意怀酷,它是個(gè)通用的命令稻爬,換句話說(shuō)在其他數(shù)據(jù)結(jié)構(gòu)中,也可以使用它 |
strlen key | 求 key 指向字符串的長(zhǎng)度 | 返回長(zhǎng)度 |
getset key value | 修改原來(lái) key 的對(duì)應(yīng)值胰坟,并將舊值返回 | 如果原來(lái)值為空因篇,則返回為空泞辐,并設(shè)置新值 |
getrange key start end | 獲取子串 | 記字符串的長(zhǎng)度為 len,把字符串看作一個(gè)數(shù)組竞滓,而 Redis 是以 0 開始計(jì)數(shù)的咐吼,所以 start 和 end 的取值范圍 為 0 到 len-1 |
append key value | 將新的字符串 value,加入到原來(lái) key 指向的字符串末 | 返回 key 指向新字符串的長(zhǎng)度 |
為了讓大家更為明確商佑,在 Redis 提供的客戶端進(jìn)行測(cè)試如圖 2 所示锯茄。
圖 2 Redis 操作字符串重用命令
這里我們看到了字符串的常用操作,為了在 Spring 中測(cè)試這些命令茶没,首先配置 Spring 關(guān)于 Redis 字符串的運(yùn)行環(huán)境肌幽,配置 Spring 關(guān)于 Redis 字符串的運(yùn)行環(huán)境代碼如下所示。
<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="yml" cid="n104" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="50" />
<property name="maxTotal" value="100" />
<property name="maxWaitMillis" value="20000" />
</bean>
?
<bean id="connectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="localhost" />
<property name="port" value="6379" />
<property name="poolConfig" ref="poolConfig" />
</bean>
<bean id="jdkSerializationRedisSerializer"
class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
<bean id="stringRedisSerializer"
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="keySerializer" ref="stringRedisSerializer" />
<property name="valueSerializer" ref="jdkSerializationRedisSerializer" />
</bean></pre>
注意抓半,這里給 Spring 的 RedisTemplate 的鍵值序列化器設(shè)置為了 String 類型喂急,所以它就是一種字符串的操作。假設(shè)把這段 Spring 的配置代碼保存為一個(gè)獨(dú)立為文件 applicationContext.xml笛求,使用 Spring 測(cè)試 Redis 字符串操作代碼如下所示廊移。
<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="java" cid="n106" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> package com.test;
?
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
?
import com.pojo.Role;
?
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"applicationContext.xml");
RedisTemplate redisTemplate = applicationContext
.getBean(RedisTemplate.class);
// 設(shè)值
redisTemplate.opsForValue().set("key1", "value1");
redisTemplate.opsForValue().set("key2", "value2");
// 通過(guò)key獲取值
String value1 = (String) redisTemplate.opsForValue().get("key1");
System.out.println(value1);
// 通過(guò)key刪除值
redisTemplate.delete("key1");
// 求長(zhǎng)度
Long length = redisTemplate.opsForValue().size("key2");
System.out.println(length);
// 設(shè)值新值并返回舊值
String oldValue2 = (String) redisTemplate.opsForValue().getAndSet(
"key2", "new_value2");
System.out.println(oldValue2);
// 通過(guò)key獲取值.
String value2 = (String) redisTemplate.opsForValue().get("key2");
System.out.println(value2);
// 求子串
String rangeValue2 = redisTemplate.opsForValue().get("key2", 0, 3);
System.out.println(rangeValue2);
// 追加字符串到末尾,返回新串長(zhǎng)度
int newLen = redisTemplate.opsForValue().append("key2", "_app");
System.out.println(newLen);
String appendValue2 = (String) redisTemplate.opsForValue().get("key2");
System.out.println(appendValue2);
}
}</pre>
這是主要的目的只是在 Spring 操作 Redis 鍵值對(duì)探入,其操作就等同于圖 2 所示的命令一樣狡孔。
在 Spring 中,redisTemplate.opsForValue() 所返回的對(duì)象可以操作簡(jiǎn)單的鍵值對(duì)蜂嗽,可以是字符串苗膝,也可以是對(duì)象,具體依據(jù)你所配置的序列化方案植旧。
由于配置 Spring 關(guān)于 Redis 字符串的運(yùn)行環(huán)境代碼所配置的是字符串辱揭,所以以字符串來(lái)操作 Redis,其測(cè)試結(jié)果如下:
圖 3 運(yùn)行結(jié)果
結(jié)果和我們看到的命令行的結(jié)果一樣的病附,作為開發(fā)者要熟悉這些方法界阁。
上面介紹了字符串最常用的命令,但是 Redis 除了這些之外還提供了對(duì)整數(shù)和浮點(diǎn)型數(shù)字的功能胖喳。如果字符串是數(shù)字(整數(shù)或者浮點(diǎn)數(shù))泡躯,那么 Redis 還能支持簡(jiǎn)單的運(yùn)算。不過(guò)它的運(yùn)算能力比較弱丽焊,目前版本只能支持簡(jiǎn)單的加減法運(yùn)算较剃,如下 。
incr key :在原字段上加 1 技健,只能對(duì)整數(shù)操作
incrby key increment : 在原字段上加上整數(shù)(increment) 写穴, 只能對(duì)整數(shù)操作
decr key :在原字段上減 1 ,只能對(duì)整數(shù)操作
decrby key decrement : 在原字段上減去整數(shù)(decrement)雌贱, 只能對(duì)整數(shù)操作
incrbyfloat keyincrement : 在原字段上加上浮點(diǎn)數(shù)(increment)啊送, 可以操作浮點(diǎn)數(shù)或者整數(shù)
對(duì)操作浮點(diǎn)數(shù)和整數(shù)進(jìn)行了測(cè)試偿短,如圖 4 所示。
圖 4 操作浮點(diǎn)數(shù)和整數(shù)
在測(cè)試過(guò)程中馋没,如果開始把 val 設(shè)置為浮點(diǎn)數(shù)昔逗,那么 incr、decr篷朵、incrby勾怒、decrby 的命令都會(huì)失敗。Redis 并不支持減法声旺、乘法笔链、除法操作,功能十分有限腮猖,這點(diǎn)需要我們注意鉴扫。
由于 Redis 的功能比較弱,所以經(jīng)常會(huì)在 Java 程序中讀取它們澈缺,然后通過(guò) Java 進(jìn)行計(jì)算并設(shè)置它們的值幔妨。
注意,所有關(guān)于減法的方法谍椅,原有值都必須是整數(shù),否則就會(huì)引發(fā)異常古话,
hash 數(shù)據(jù)結(jié)構(gòu)和常用命令
Redis 中哈希(hash)結(jié)構(gòu)就如同 Java 的 map 一樣雏吭,一個(gè)對(duì)象里面有許多鍵值對(duì),它是特別適合存儲(chǔ)對(duì)象的陪踩,如果內(nèi)存足夠大杖们,那么一個(gè) Redis 的 hash 結(jié)構(gòu)可以存儲(chǔ) 2 的 32 次方減 1 個(gè)鍵值對(duì)(40 多億)。
一般而言肩狂,不會(huì)使用到那么大的一個(gè)鍵值對(duì)摘完,所以我們認(rèn)為 Redis 可以存儲(chǔ)很多的鍵值對(duì)。在 Redis 中傻谁,hash 是一個(gè) String 類型的 field 和 value 的映射表孝治,因此我們存儲(chǔ)的數(shù)據(jù)實(shí)際在 Redis 內(nèi)存中都是一個(gè)個(gè)字符串而已。
Redis hash 結(jié)構(gòu)命令审磁,如下谈飒。
命 令 | 說(shuō) 明 | 備 注 |
---|---|---|
hdel key field1[field2......] | 刪除 hash 結(jié)構(gòu)中的某個(gè)(些)字段 | 可以進(jìn)行多個(gè)字段的刪除 |
hexists key field | 判斷 hash 結(jié)構(gòu)中是否存在 field 字段 | 存在返回 1,否則返回 0 |
hgetall key | 獲取所有 hash 結(jié)構(gòu)中的鍵值 | 返回鍵和值 |
hincrby key field increment | 指定給 hash 結(jié)構(gòu)中的某一字段加上一個(gè)整數(shù) | 要求該字段也是整數(shù)字符串 |
hincrbyfloat key field increment | 指定給 hash 結(jié)構(gòu)中的某一字段加上一個(gè)浮點(diǎn)數(shù) | 要求該字段是數(shù)字型字符串 |
hkeys key | 返回 hash 中所有的鍵 | —— |
hlen key | 返回 hash 中鍵值對(duì)的數(shù)量 | —— |
hmget key field1[field2......] | 返回 hash 中指定的鍵的值态蒂,可以是多個(gè) | 依次返回值 |
hmset key field1 value1 [field2 field2......] | hash 結(jié)構(gòu)設(shè)置多個(gè)鍵值對(duì) | —— |
hset key filed value | 在 hash 結(jié)構(gòu)中設(shè)置鍵值對(duì) | 單個(gè)設(shè)值 |
hsetnx key field value | 當(dāng) hash 結(jié)構(gòu)中不存在對(duì)應(yīng)的鍵杭措,才設(shè)置值 | —— |
hvals key | 獲取 hash 結(jié)構(gòu)中所有的值 | —— |
可以看出,在 Redis 中的哈希結(jié)構(gòu)和字符串有著比較明顯的不同钾恢。
首先手素,命令都是以 h 開頭鸳址,代表操作的是 hash 結(jié)構(gòu)。其次泉懦,大多數(shù)命令多了一個(gè)層級(jí) field稿黍,這是 hash 結(jié)構(gòu)的一個(gè)內(nèi)部鍵,也就是說(shuō) Redis 需要通過(guò) key 索引到對(duì)應(yīng)的 hash 結(jié)構(gòu)祠斧,再通過(guò) field 來(lái)確定使用 hash 結(jié)構(gòu)的哪個(gè)鍵值對(duì)闻察。
下面通過(guò) Redis 的這些操作命令來(lái)展示如何使用它們,如圖 2 所示琢锋。
圖 2 Redis 的 hash 結(jié)構(gòu)命令展示
從圖 2 中可以看到辕漂,Redis 關(guān)于哈希結(jié)構(gòu)的相關(guān)命令。這里需要注意的是:
哈希結(jié)構(gòu)的大小吴超,如果哈希結(jié)構(gòu)是個(gè)很大的鍵值對(duì)钉嘹,那么使用它要十分注意,尤其是關(guān)于 hkeys鲸阻、hgetall跋涣、hvals 等返回所有哈希結(jié)構(gòu)數(shù)據(jù)的命令,會(huì)造成大量數(shù)據(jù)的讀取鸟悴。這需要考慮性能和讀取數(shù)據(jù)大小對(duì) JVM 內(nèi)存的影響陈辱。
對(duì)于數(shù)字的操作命令 hincrby 而言,要求存儲(chǔ)的也是整數(shù)型的字符串细诸,對(duì)于 hincrbyfloat 而言沛贪,則要求使用浮點(diǎn)數(shù)或者整數(shù),否則命令會(huì)失敗震贵。
我們用 Spring 來(lái)完成圖 2 的功能利赋,代碼如下所示。
<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="java" cid="n200" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> public static void testRedisHash() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
RedisTemplate redisTemplate = applicationcontext.getBean(RedisTemplate.class);
String key = "hash";
Map<String, String> map = new HashMap<String,String>();
map.put("f1", "val1");
map.put("f2", "val2");
// 相當(dāng)于hmset命令
redisTemplate.opsForHash().putAll(key, map);
// 相當(dāng)于hset命令
redisTemplate.opsForHash().put(key, "f3", "6");
printValueForhash (redisTemplate, key, "f3");
// 相當(dāng)于 hexists key filed 命令
boolean exists = redisTemplate.opsForHash().hasKey(key, "f3");
System.out.println(exists);
// 相當(dāng)于hgetall命令
Map keyValMap = redisTemplate.opsForHash().entries(key);
//相當(dāng)于hincrby命令
redisTemplate.opsForHash().increment(key, "f3",2);
printValueForhash (redisTemplate, key, "f3");
//相當(dāng)于hincrbyfloat命令
redisTemplate.opsForHash().increment (key, "f3", 0.88);
printValueForhash(redisTemplate, key, "f3");
//相當(dāng)于hvals命令
List valueList = redisTemplate.opsForHash().values(key);
//相當(dāng)于hkeys命令
Set keyList = redisTemplate.opsForHash().keys(key);
List<String> fieldList = new ArrayList<String>();
fieldList.add("f1");
fieldList.add("f2");
//相當(dāng)于hmget命令
List valueList2 = redisTemplate.opsForHash().multiGet(key, keyList);
//相當(dāng)于hsetnx命令
boolean success = redisTemplate.opsForHash () .putlfAbsent(key, "f4", "val4");
System.out.println(success);
//相當(dāng)于hdel命令
Long result = redisTemplate.opsForHash().delete(key, "fl", "f2");
System.out.println(result);
}
private static void printValueForhash(RedisTemplate redisTemplate,String key,String field) {
//相當(dāng)于hget命令
Object value = redisTemplate.opsForHash().get(key,field);
System.out.println(value);
}</pre>
以上代碼做了比較詳細(xì)的注解猩系,也不難理解媚送,不過(guò)需要注意以下幾點(diǎn)內(nèi)容:
hmset 命令,在 Java 的 API 中寇甸,是使用 map 保存多個(gè)鍵值對(duì)在先的塘偎。
hgetall 命令會(huì)返回所有的鍵值對(duì),并保存到一個(gè) map 對(duì)象中拿霉,如果 hash 結(jié)構(gòu)很大式塌,那么要考慮它對(duì) JVM 的內(nèi)存影響。
hincrby 和 hincrbyFloat 命令都采用 increment 方法友浸,Spring 會(huì)識(shí)別它具體使用何種方法峰尝。
redisTemplate.opsForHash().values(key) 方法相當(dāng)于 hvals 命令,它會(huì)返回所有的值收恢,并保存到一個(gè) List 對(duì)象中武学;而 redisTemplate.opsForHash().keys(key) 方法相當(dāng)于 hkeys 命令祭往,它會(huì)獲取所有的鍵,保存到一個(gè) Set 對(duì)象中火窒。
在 Spring 中使用 redisTemplate.opsForHash().putAll(key,map) 方法相當(dāng)于執(zhí)行了 hmset 命令硼补,使用了 map,由于配置了默認(rèn)的序列化器為字符串熏矿,所以它也只會(huì)用字符串進(jìn)行轉(zhuǎn)化已骇,這樣才能執(zhí)行對(duì)應(yīng)的數(shù)值加法,如果使用其他序列化器票编,則后面的命令可能會(huì)拋出異常褪储。
在使用大的 hash 結(jié)構(gòu)時(shí),需要考慮返回?cái)?shù)據(jù)的大小慧域,以避免返回太多的數(shù)據(jù)鲤竹,引發(fā) JVM 內(nèi)存溢出或者 Redis 的性能問(wèn)題。
運(yùn)行以上代碼昔榴,可以得到這樣的輸出結(jié)果:
圖 3 運(yùn)行結(jié)果
操作成功了辛藻,按照類似的代碼就可以在 Spring 中順利操作 Redis 的 hash 結(jié)構(gòu)了。
List 數(shù)據(jù)結(jié)構(gòu)和常用命令
鏈表(List)結(jié)構(gòu)是 Redis 中一個(gè)常用的結(jié)構(gòu)互订,它可以存儲(chǔ)多個(gè)字符串吱肌,而且它是有序的,能夠存儲(chǔ) 2 的 32 次方減 1 個(gè)節(jié)點(diǎn)(超過(guò) 40 億個(gè)節(jié)點(diǎn))仰禽。
Redis 鏈表是雙向的氮墨,因此即可以從左到右,也可以從右到左遍歷它存儲(chǔ)的節(jié)點(diǎn)坟瓢,鏈表結(jié)構(gòu)如圖 1 所示。
圖 1 鏈表結(jié)構(gòu)
由于是雙向鏈表犹撒,所以只能夠從左到右折联,或者從右到左地訪問(wèn)和操作鏈表里面的數(shù)據(jù)節(jié)點(diǎn)。但是使用鏈表結(jié)構(gòu)就意味著讀性能的喪失识颊,所以要在大量數(shù)據(jù)中找到一個(gè)節(jié)點(diǎn)的操作性能是不佳的诚镰,因?yàn)殒湵碇荒軓囊粋€(gè)方向中去遍歷所要節(jié)點(diǎn)。
比如從查找節(jié)點(diǎn) 10 000 開始查詢祥款,它需要按照節(jié)點(diǎn) 1清笨、節(jié)點(diǎn) 2、節(jié)點(diǎn) 3……直至節(jié)點(diǎn) 10 000刃跛,這樣的順序查找抠艾,然后把一個(gè)個(gè)節(jié)點(diǎn)和你給出的值比對(duì),才能確定節(jié)點(diǎn)所在桨昙。如果這個(gè)鏈表很大检号,如有上百萬(wàn)個(gè)節(jié)點(diǎn)腌歉,可能需要遍歷幾十萬(wàn)次才能找到所需要的節(jié)點(diǎn),顯然查找性能是不佳的齐苛。
而鏈表結(jié)構(gòu)的優(yōu)勢(shì)在于插入和刪除的便利翘盖,因?yàn)殒湵淼臄?shù)據(jù)節(jié)點(diǎn)是分配在不同的內(nèi)存區(qū)域的,并不連續(xù)凹蜂,只是根據(jù)上一個(gè)節(jié)點(diǎn)保存下一個(gè)節(jié)點(diǎn)的順序來(lái)索引而已馍驯,無(wú)需移動(dòng)元素。其新增和刪除的操作如圖 2 所示玛痊。
圖 2 鏈表的新增和刪除操作
圖 2 的阿拉伯?dāng)?shù)字代表新增的步驟汰瘫,而漢字?jǐn)?shù)字代表刪除步驟。
新增節(jié)點(diǎn):對(duì)插入圖中的節(jié)點(diǎn) 4 而言卿啡,先看從左到右的指向吟吝,先讓節(jié)點(diǎn) 4 指向節(jié)點(diǎn) 1 原來(lái)的下一個(gè)節(jié)點(diǎn),也就是節(jié)點(diǎn) 2颈娜,然后讓節(jié)點(diǎn) 1 指向節(jié)點(diǎn) 4剑逃,這樣就完成了從右到左的指向修改。再看從右到左官辽,先讓節(jié)點(diǎn) 4 指向節(jié)點(diǎn) 1蛹磺,然后節(jié)點(diǎn) 2 指向節(jié)點(diǎn) 4,這個(gè)時(shí)候就完成了從右到左的指向同仆,那么節(jié)點(diǎn) 1 和節(jié)點(diǎn) 2 之間的原有關(guān)聯(lián)關(guān)系都已經(jīng)失效萤捆,這樣就完成了在鏈表中新增節(jié)點(diǎn)4的功能。
刪除節(jié)點(diǎn):對(duì)刪除圖中的節(jié)點(diǎn) 3 而言俗批,首先讓節(jié)點(diǎn) 2 從左到右指向后續(xù)節(jié)點(diǎn)俗或,然后讓后續(xù)節(jié)點(diǎn)指向節(jié)點(diǎn) 2,這樣節(jié)點(diǎn) 3 就脫離了鏈表岁忘,也就是斷絕了與節(jié)點(diǎn) 2 和后繼節(jié)點(diǎn)的關(guān)聯(lián)關(guān)系辛慰,然后對(duì)節(jié)點(diǎn) 3 進(jìn)行內(nèi)存回收,無(wú)須移動(dòng)任何節(jié)點(diǎn)干像,就完成了刪除帅腌。
由此可見(jiàn),鏈表結(jié)構(gòu)的使用是需要注意場(chǎng)景的麻汰,對(duì)于那些經(jīng)常需要對(duì)數(shù)據(jù)進(jìn)行插入和刪除的列表數(shù)據(jù)使用它是十分方便的速客,因?yàn)樗梢栽诓灰苿?dòng)其他節(jié)點(diǎn)的情況下完成插入和刪除。而對(duì)于需要經(jīng)常查找的五鲫,使用它性能并不佳溺职,它只能從左到右或者從右到左的查找和比對(duì)。
因?yàn)槭请p向鏈表結(jié)構(gòu),所以 Redis 鏈表命令分為左操作和右操作兩種命令辅愿,左操作就意味著是從左到右智亮,右操作就意味著是從右到左。
Redis 關(guān)于鏈表的命令如表 1 所示点待。
命 令 | 說(shuō) 明 | 備 注 | |
---|---|---|---|
lpush key node1 [node2.]..... | 把節(jié)點(diǎn) node1 加入到鏈表最左邊 | 如果是 node1阔蛉、node2 ...noden 這樣加入, 那么鏈表開頭從左到右的順序是 noden...node2癞埠、node1 | |
rpush key node1[node2]...... | 把節(jié)點(diǎn) node1 加入到鏈表的最右邊 | 如果是 node1状原、node2....noden 這樣加 入,那么鏈表結(jié)尾從左到右的順序是 node1苗踪、node2,node3...noden | |
lindex key index | 讀取下標(biāo)為 index 的節(jié)點(diǎn) | 返回節(jié)點(diǎn)字符串颠区,從 0 開始算 | |
llen key | 求鏈表的長(zhǎng)度 | 返回鏈表節(jié)點(diǎn)數(shù) | |
lpop key | 刪除左邊第一個(gè)節(jié)點(diǎn),并將其返回 | —— | |
rpop key | 刪除右邊第一個(gè)節(jié)點(diǎn)通铲,并將其返回 | —— | |
linsert key before | after pivot node | 插入一個(gè)節(jié)點(diǎn) node毕莱,并且可以指定在值為pivot 的節(jié)點(diǎn)的前面(before)或者后面(after)) | 如果 list 不存在,則報(bào)錯(cuò)颅夺;如果沒(méi)有值為對(duì)應(yīng) pivot 的朋截,也會(huì)插入失敗返回 -1 |
lpushx list node | 如果存在 key 為 list 的鏈表,則插入節(jié)點(diǎn) node, 并且作為從左到右的第一個(gè)節(jié)點(diǎn) | 如果 list 不存在吧黄,則失敗 | |
rpushx list node | 如果存在 key 為 list 的鏈表部服,則插入節(jié)點(diǎn) node,并且作為從左到右的最后個(gè)節(jié)點(diǎn) | 如果 list 不存在拗慨,則失敗 | |
lrange list start end | 獲取鏈表 list 從 start 下標(biāo)到 end 下標(biāo)的節(jié)點(diǎn)值 | 包含 start 和 end 下標(biāo)的值 | |
lrem list count value | 如果 count 為 0廓八,則刪除所有值等于 value 的節(jié) 點(diǎn):如果 count 不是 0,則先對(duì) count 取絕對(duì)值赵抢,假設(shè)記為 abs剧蹂,然后從左到右刪除不大于 abs 個(gè)等于 value 的節(jié)點(diǎn) | 注意,count 為整數(shù),如果是負(fù)數(shù),則 Redis 會(huì)先求取其絕對(duì)值威始,然后傳遞到后臺(tái)操作 | |
lset key index node | 設(shè)置列表下標(biāo)為 index 的節(jié)點(diǎn)的值為 node | —— | |
ltrim key start stop | 修剪鏈表,只保留從 start 到 stop 的區(qū)間的節(jié)點(diǎn)车吹,其余的都刪除掉 | 包含 start 和 end 的下標(biāo)的節(jié)點(diǎn)會(huì)保留 |
表所列舉的就是常用的鏈表命令筹裕,其中以“l(fā)”開頭的代表左操作醋闭,以“r”開頭的代表右操作。對(duì)于很多個(gè)節(jié)點(diǎn)同時(shí)操作的朝卒,需要考慮其花費(fèi)的時(shí)間证逻,鏈表數(shù)據(jù)結(jié)構(gòu)對(duì)于查找而言并不適合于大數(shù)據(jù),而 Redis 也給了比較靈活的命令對(duì)其進(jìn)行操作抗斤。Redis 關(guān)于鏈表的操作命令囚企,如圖 3 所示丈咐。
圖 3 Redis關(guān)于鏈表的操作命令
這里展示了關(guān)于 Redis 鏈表的常用命令,只是對(duì)于大量數(shù)據(jù)操作的時(shí)候龙宏,我們需要考慮插入和刪除內(nèi)容的大小棵逊,因?yàn)檫@將是十分消耗性能的命令,會(huì)導(dǎo)致 Redis 服務(wù)器的卡頓银酗。對(duì)于不允許卡頓的一些服務(wù)器辆影,可以進(jìn)行分批次操作,以避免出現(xiàn)卡頓黍特。
需要指出的是蛙讥,之前這些操作鏈表的命令都是進(jìn)程不安全的,因?yàn)楫?dāng)我們操作這些命令的時(shí)候灭衷,其他 Redis 的客戶端也可能操作同一個(gè)鏈表次慢,這樣就會(huì)造成并發(fā)數(shù)據(jù)安全和一致性的問(wèn)題,尤其是當(dāng)你操作一個(gè)數(shù)據(jù)量不小的鏈表結(jié)構(gòu)時(shí)翔曲,常常會(huì)遇到這樣的問(wèn)題迫像。
為了克服這些問(wèn)題,Redis 提供了鏈表的阻塞命令部默,它們?cè)谶\(yùn)行的時(shí)候侵蒙,會(huì)給鏈表加鎖,以保證操作鏈表的命令安全性傅蹂,如表所示纷闺。
命 令 | 說(shuō) 明 | 備 注 |
---|---|---|
blpop key timeout | 移出并獲取列表的第一個(gè)元索,如果列表沒(méi)有元素會(huì)阻塞列表直到等待超時(shí)或發(fā)現(xiàn)可彈出元索為止 | 相對(duì)于 lpop 命令份蝴,它的操作是進(jìn)程安全的 |
brpop key timeout | 移出并獲取列表的最后一個(gè)元素犁功,如果列表沒(méi)有元素會(huì)阻塞列表直到等待超時(shí)或發(fā)現(xiàn)可彈出元素為止 | 相對(duì)于 rpop 命令,它的操作是進(jìn)程安全的 |
rpoplpush key sre dest | 按從左到右的順序婚夫,將一個(gè)鏈表的最后一個(gè)元素移除浸卦,并插入到目標(biāo)鏈表最左邊 | 不能設(shè)置超時(shí)時(shí)間 |
brpoplpush key src dest timeout | 按從左到右的順序,將一個(gè)鏈表的最后一個(gè)元素移除案糙,并插入到目標(biāo)鏈表最左邊限嫌,并可以設(shè)置超時(shí)時(shí)間 | 可設(shè)置超時(shí)時(shí)間 |
當(dāng)使用這些命令時(shí),Redis 就會(huì)對(duì)對(duì)應(yīng)的鏈表加鎖时捌,加鎖的結(jié)果就是其他的進(jìn)程不能再讀取或者寫入該鏈表怒医,只能等待命令結(jié)束。加鎖的好處可以保證在多線程并發(fā)環(huán)境中數(shù)據(jù)的一致性奢讨,保證一些重要數(shù)據(jù)的一致性稚叹,比如賬戶的金額、商品的數(shù)量。
不過(guò)在保證這些的同時(shí)也要付出其他線程等待扒袖、線程環(huán)境切換等代價(jià)塞茅,這將使得系統(tǒng)的并發(fā)能力下降,關(guān)于多線程并發(fā)鎖季率,未來(lái)還會(huì)提及野瘦,這里先看 Redis 鏈表阻塞操作命令,如圖 4 所示飒泻。
圖 4 Redis 鏈表阻塞操作命令
在實(shí)際的項(xiàng)目中缅刽,雖然阻塞可以有效保證了數(shù)據(jù)的一致性,但是阻塞就意味著其他進(jìn)程的等待蠢络,CPU 需要給其他線程掛起衰猛、恢復(fù)等操作,更多的時(shí)候我們希望的并不是阻塞的處理請(qǐng)求刹孔,所以這些命令在實(shí)際中使用得并不多啡省,后面還會(huì)深入探討關(guān)于高并發(fā)鎖的問(wèn)題。
使用 Spring 去操作 Redis 鏈表的命令髓霞,這里繼續(xù)保持代碼清單18-5關(guān)于 RedisTemplate 的配置卦睹,在此基礎(chǔ)上獲取 RedisTemplate 對(duì)象,然后輸入以下代碼方库,它實(shí)現(xiàn)的是圖 3 所示的命令功能结序,請(qǐng)讀者仔細(xì)體會(huì)。
<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="java" cid="n333" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> public static void testList() {
ApplicationContext applicationcontext = new ClassPathXmlApplicationContext("applicationContext.xml");
RedisTemplate redisTemplate = applicationcontext.getBean(RedisTemplate.class);
try {
//刪除鏈表纵潦,以便我們可以反復(fù)測(cè)試
redisTemplate.delete("list");
//把node3插入鏈表list
redisTemplate. opsForList ().leftPush ("list", "node3");
List<String> nodeList = new ArrayList<String>();
for (int i = 2; i >= 1; i--){
nodeList.add("nnode" + i);
}
//相當(dāng)于lpush把多個(gè)價(jià)值從左插入鏈表
redisTemplate.opsForList().leftPushAll("list", nodeList);
//從右邊插入一個(gè)節(jié)點(diǎn)
redisTemplate.opsForList().rightPush("list", "node4");
//獲取下標(biāo)為0的節(jié)點(diǎn)
String nodel = (String) redisTemplate.opsForList() .index("list", 0);
//獲取鏈表長(zhǎng)度
long size = redisTemplate.opsForList ().size ("listn");
//從左邊彈出一個(gè)節(jié)點(diǎn)
String lpop = (String) redisTemplate.opsForList().leftPop("list");
//從右邊彈出一個(gè)節(jié)點(diǎn)
String rpop = (String) redisTemplate.opsForList().rightPop("list");
//注意徐鹤,需要使用更為底層的命令才能操作linsert命令
//使用linsert命令在node2前插入一個(gè)節(jié)點(diǎn)
redisTemplate.getConnectionFactory().getConnection().lInsert("list".getBytes("utf-8"),RedisListCommands.Position.BEFORE,"node2".getBytes("utf-8"),"before_node".getBytes("utf-8"));
//使用linsert命令在node2后插入一個(gè)節(jié)點(diǎn)
redisTemplate.getConnectionFactory().getConnection().linsert("list".getBytes("utf-8"),RedisListCommands.Position.AFTER,"node2".getBytes("utf-8"), "after_node".getBytes("utf-8"));
//判斷l(xiāng)ist是否存在,如果存在則從左邊插入head節(jié)點(diǎn)
redisTemplate.opsForList().leftPushlfPresent("list", "head");
//判斷l(xiāng)ist是否存在邀层,如果存在則從右邊插入end節(jié)點(diǎn)
redisTemplate.opsForList().rightPushlfPresent("list", "end");
//從左到右返敬,或者下標(biāo)從0到10的節(jié)點(diǎn)元素
List valueList = redisTemplate.opsForList().range("list", 0, 10);
nodeList.clear();
for (int i = 1; i <= 3; i++) {
nodeList.add("node");
}
//在鏈表左邊插入三個(gè)值為node的節(jié)點(diǎn)
redisTemplate.opsForList().leftPushAll.("list", nodeList);
//從左到右刪除至多三個(gè)node節(jié)點(diǎn)
redisTemplate.opsForList().remove("list", 3,"node");
//給鏈表下標(biāo)為0的節(jié)點(diǎn)設(shè)置新值
redisTemplate.opsForList().set("list",0, "new_head_value");
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
}
//打印鏈表數(shù)據(jù)
printList(redisTemplate, "list");
}
public static void printList(RedisTemplate redisTemplate, String key) {
//鏈表長(zhǎng)度
Long size = redisTemplate.opsForList().size(key);
}</pre>
這里所展示的是 RedisTemplate 對(duì)于 Redis 鏈表的操作,其中 left 代表左操作寥院,right 代表右操作劲赠。有些命令 Spring 所提供的 RedisTemplate 并不能支持,比如 linsert 命令秸谢,這個(gè)時(shí)候可以使用更為底層的方法去操作凛澎,正如代碼中的這段:
<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="java" cid="n335" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> // 使用linsert命令在node2前插入一個(gè)節(jié)點(diǎn)
redisTemplate.getConnectionFactory().getConnection().lInsert("list".getBytes("utf-8"),
RedisListCommands.Position.BEFORE,"node2".getBytes("utf-8"),
"before_node".getBytes("utf-8"));</pre>
在多值操作的時(shí)候,往往會(huì)使用 list 進(jìn)行封裝估蹄,比如 leftPushAll 方法塑煎,對(duì)于很大的 list 的操作需要注意性能,比如 remove 這樣的操作元媚,在大的鏈表中會(huì)消耗 Redis 系統(tǒng)很多的性能轧叽。
正如之前的探討一樣,Redis 還有對(duì)鏈表進(jìn)行阻塞操作的命令刊棕,這里 Spring 也給出了支持炭晒,代碼如下所示。
<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="java" cid="n338" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> public static void testBList() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
// 清空數(shù)據(jù)甥角,可以重復(fù)測(cè)試
redisTemplate.delete ("list1");
redisTemplate.delete ("list2");
//初始化鏈表 list1
List<String> nodeList = new ArrayList<String>();
for (int i=1; i<=5; i++) {
nodeList.add("node" + i);
}
redisTemplate.opsForList().leftPushAll("list1", nodeList);
// Spring 使用參數(shù)超時(shí)時(shí)間作為阻塞命令區(qū)分网严,等價(jià)于 blpop 命令,并且可以設(shè)置時(shí)間參數(shù) redisTemplate.opsForList().leftPop ("list1", 1, TimeUnit.SECONDS);
// Spring 使用參數(shù)超時(shí)時(shí)間作為阻塞命令區(qū)分嗤无,等價(jià)于 brpop 命令震束,并且可以設(shè)置時(shí)間參數(shù)
redisTemplate.opsForList().rightPop("list1", 1, TimeUnit.SECONDS);
nodeList.clear();
// 初始化鏈表 list2
for (int i=1; i<=3; i++) {
nodeList.add("dato" + i);
}
redisTemplate.opsForList().leftPushAll("list2", nodeList);
// 相當(dāng)于 rpoplpush 命令,彈出 list1 最右邊的節(jié)點(diǎn)当犯,插入到 list2 最左邊
redisTemplate.opsForList().rightPopAndLeftPush("list1","list2");
// 相當(dāng)于brpoplpush命令垢村,注意在 Spring 中使用超時(shí)參數(shù)區(qū)分 redisTemplate.opsForList().rightPopAndLeftPush("list1", "list2",1,TimeUnit.SECONDS);
// 打印鏈表數(shù)據(jù)
printList(redisTemplate, "list1");
printList(redisTemplate, "list2");
}</pre>
這里展示了 Redis 關(guān)于鏈表的阻塞命令,在 Spring 中它和非阻塞命令的方法是一致的嚎卫,只是它會(huì)通過(guò)超時(shí)參數(shù)進(jìn)行區(qū)分嘉栓,而且我們還可以通過(guò)方法設(shè)置時(shí)間的單位,使用還是相當(dāng)簡(jiǎn)單的拓诸。注意侵佃,它是阻塞的命令,在多線程的環(huán)境中奠支,它能在一定程度上保證數(shù)據(jù)的一致而性能卻不佳馋辈。
Set 數(shù)據(jù)結(jié)構(gòu)和常用命令
Redis 的集合(set)不是一個(gè)線性結(jié)構(gòu),而是一個(gè)哈希表結(jié)構(gòu)倍谜,它的內(nèi)部會(huì)根據(jù) hash 分子來(lái)存儲(chǔ)和查找數(shù)據(jù)迈螟,理論上一個(gè)集合可以存儲(chǔ) 2 的 32 次方減 1 個(gè)節(jié)點(diǎn)(大約 42 億)個(gè)元素,因?yàn)椴捎霉1斫Y(jié)構(gòu)尔崔,所以對(duì)于 Redis 集合的插入井联、刪除和查找的復(fù)雜度都是 0(1),只是我們需要注意 3 點(diǎn)您旁。
對(duì)于集合而言烙常,它的每一個(gè)元素都是不能重復(fù)的,當(dāng)插入相同記錄的時(shí)候都會(huì)失敗鹤盒。
集合是無(wú)序的蚕脏。
集合的每一個(gè)元素都是 String 數(shù)據(jù)結(jié)構(gòu)類型。
Redis 的集合可以對(duì)于不同的集合進(jìn)行操作侦锯,比如求出兩個(gè)或者以上集合的交集驼鞭、差集和并集等。集合命令尺碰,如表 所示挣棕。
命 令 | 說(shuō) 明 | 備 注 |
---|---|---|
sadd key member1 [member2 member3......] | 給鍵為 key 的集合増加成員 | 可以同時(shí)増加多個(gè) |
scard key | 統(tǒng)計(jì)鍵為 key 的集合成員數(shù) | — |
sdiffkey1 [key2] | 找出兩個(gè)集合的差集 | 參數(shù)如果是單key译隘,那么 Redis 就返回這個(gè) key 的所有元素 |
sdiftstore des key1 [key2] | 先按 sdiff 命令的規(guī)則,找出 key1 和 key2 兩 個(gè)集合的差集洛心,然后將其保存到 des 集合中固耘。 | — |
sinter key1 [key2] | 求 key1 和 key2 兩個(gè)集合的交集。 | 參數(shù)如果是單 key词身,那么 Redis 就返冋這個(gè) key 的所有元素 |
sinterstore des key1 key2 | 先按 sinter 命令的規(guī)則厅目,找出 key1 和 key2 兩個(gè)集合的交集,然后保存到 des 中 | — |
sismember key member | 判斷 member 是否鍵為 key 的集合的成員 | 如果是返回 1法严,否則返回 0 |
smembers key | 返回集合所有成員 | 如果數(shù)據(jù)量大损敷,需要考慮迭代遍歷的問(wèn)題 |
smove src des member | 將成員 member 從集合 src 遷移到集合 des 中 | — |
spop key | 隨機(jī)彈出集合的一個(gè)元素 | 注意其隨機(jī)性,因?yàn)榧鲜菬o(wú)序的 |
srandmember key [count] | 隨機(jī)返回集合中一個(gè)或者多個(gè)元素深啤,count 為限制返回總數(shù)拗馒,如果 count 為負(fù)數(shù),則先求其絕對(duì)值 | count 為整數(shù)溯街,如果不填默認(rèn)為 1瘟忱,如果 count 大于等于集合總數(shù),則返回整個(gè)集合 |
srem key member1[member2......] | 移除集合中的元素苫幢,可以是多個(gè)元素 | 對(duì)于很大的集合可以通過(guò)它刪除部分元素访诱,避免刪除大量數(shù)據(jù)引發(fā) Redis 停頓 |
sunion key1 [key2] | 求兩個(gè)集合的并集 | 參數(shù)如果是單 key,那么 Redis 就返回這個(gè) key 的所有元素 |
sunionstore des key1 key2 | 先執(zhí)行 sunion 命令求出并集韩肝,然后保存到鍵為 des 的集合中 | — |
表 中命令的前綴都包含了一個(gè) s触菜,用來(lái)表達(dá)這是集合的命令,集合是無(wú)序的哀峻,并且支持并集涡相、交集和差集的運(yùn)算,下面通過(guò)命令行客戶端來(lái)演示這些命令剩蟀,如圖 1 所示催蝗。
圖 1 通過(guò)命令行客戶端演示這些命令
交集、并集和差集保存命令的用法育特,如下圖 2 所示丙号。
圖 2 交集缰冤、并集和差集保存命令的用法
這里的命令主要是求差集犬缨、并集和交集,并保存到新的集合中棉浸。至此就展示了表 1 中的所有命令怀薛,下面將在 Spring 中操作它們,代碼如下所示迷郑。
<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="java" cid="n420" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> // 請(qǐng)把 RedisTemplate 值序列化器設(shè)置為 StringRedisSerializer 測(cè)試該代碼片段
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
Set set = null;
// 將元素加入列表
redisTemplate.boundSetOps ("set1").add ("vl","v2","v3","v4","v5", "v6");
redisTemplate.boundSetOps ("set2").add( "v0","v2","v4","v6","v8");
//求集合長(zhǎng)度
redisTemplate.opsForSet().size ("set1");
//求差集
set = redisTemplate.opsForSet().difference ("set1","set2");
//求并集
set = redisTemplate.opsForSet().intersect ("set1","set2");
//判斷是否集合中的元素
boolean exists = redisTemplate.opsForSet().isMember("set1", "vl");
//獲取集合所有元素
set = redisTemplate.opsForSet().members ("set1");
//從集合中隨機(jī)彈出一個(gè)元素
String val = (String)redisTemplate.opsForSet().pop("set1");
//隨機(jī)獲取一個(gè)集合的元素
val = (String) redisTemplate.opsForSet().randomMember("set1");
//隨機(jī)獲取2個(gè)集合的元素
List list = redisTemplate.opsForSet ().randomMembers ("set1", 2L);
//刪除一個(gè)集合的元素枝恋,參數(shù)可以是多個(gè)
redisTemplate.opsForSet().remove ("setl","v1");
//求兩個(gè)集合的并集
redisTemplate.opsForSet().union ("set1","set2");
//求兩個(gè)集合的差集创倔,并保存到集合diff_set中
redisTemplate.opsForSet().differenceAndStore("set1", "set2", "diff_set");
//求兩個(gè)集合的交集,并保存到集合inter_set中
redisTemplate.opsForSet().intersectAndStore("set1","set2" "inter_set");
//求兩個(gè)集合的并集焚碌,并保存到集合union_set中
redisTemplate.opsForSet().unionAndStore ("set1", "set2", "union_set");</pre>
上面的注釋已經(jīng)較為詳細(xì)地描述了代碼的含義畦攘,這樣我們就可以在實(shí)踐中使用 Spring 操作 Redis 的集合了。
Zset 數(shù)據(jù)結(jié)構(gòu)和常用命令
有序集合(zset)和集合(set)類似呐能,只是說(shuō)它是有序的,和無(wú)序集合的主要區(qū)別在于每一個(gè)元素除了值之外抑堡,它還會(huì)多一個(gè)分?jǐn)?shù)摆出。分?jǐn)?shù)是一個(gè)浮點(diǎn)數(shù),在 Java 中是使用雙精度表示的首妖,根據(jù)分?jǐn)?shù)偎漫,Redis 就可以支持對(duì)分?jǐn)?shù)從小到大或者從大到小的排序。
這里和無(wú)序集合一樣有缆,對(duì)于每一個(gè)元素都是唯一的象踊,但是對(duì)于不同元素而言,它的分?jǐn)?shù)可以一樣棚壁。元素也是 String 數(shù)據(jù)類型杯矩,也是一種基于 hash 的存儲(chǔ)結(jié)構(gòu)。
集合是通過(guò)哈希表實(shí)現(xiàn)的袖外,所以添加史隆、刪除、查找的復(fù)雜度都是 0(1)曼验。集合中最大的成員數(shù)為 2 的 32 次方減 1(40 多億個(gè)成員)泌射,有序集合的數(shù)據(jù)結(jié)構(gòu)如圖 1 所示。
圖 1 有序集合的數(shù)據(jù)結(jié)構(gòu)
有序集合是依賴 key 標(biāo)示它是屬于哪個(gè)集合鬓照,依賴分?jǐn)?shù)進(jìn)行排序熔酷,所以值和分?jǐn)?shù)是必須的,而實(shí)際上不僅可以對(duì)分?jǐn)?shù)進(jìn)行排序豺裆,在滿足一定的條件下拒秘,也可以對(duì)值進(jìn)行排序。
有序集合和無(wú)序集合的命令是接近的臭猜,只是在這些命令的基礎(chǔ)上翼抠,會(huì)增加對(duì)于排序的操作,這些是我們?cè)谑褂玫臅r(shí)候需要注意的細(xì)節(jié)获讳。
有些時(shí)候 Redis 借助數(shù)據(jù)區(qū)間的表示方法來(lái)表示包含或者不包含阴颖,比如在數(shù)學(xué)的區(qū)間表示中,[2,5] 表示包含 2丐膝,但是不包含 5 的區(qū)間量愧。具體如表所示钾菊。
命 令 | 說(shuō) 明 | 備 注 |
---|---|---|
zadd key score1 value1 [score2 value2......] | 向有序集合的 key,增加一個(gè)或者多個(gè)成員 | 如果不存在對(duì)應(yīng)的 key偎肃,則創(chuàng)建鍵為 key 的有序集合 |
zcard key | 獲取有序集合的成員數(shù) | — |
zcount key min max | 根據(jù)分?jǐn)?shù)返回對(duì)應(yīng)的成員列表 | min 為最小值煞烫,max 為最大值,默認(rèn)為包含 min 和 max 值累颂,采用數(shù)學(xué)區(qū)間表示的方法滞详,如果需要不包含,則在分?jǐn)?shù)前面加入“(”紊馏,注意不支持“[”表示 |
zincrby key increment member | 給有序集合成員值為 member 的分?jǐn)?shù)增加 increment | — |
zinterstore desKey numkeys key1 [key2 key3......] | 求多個(gè)有序集合的交集料饥,并且將結(jié)果保存到 desKey 中 | numkeys 是一個(gè)整數(shù),表示多少個(gè)有序集合 |
zlexcount key min max | 求有序集合 key 成員值在 min 和 max 的范圍 | 這里范圍為 key 的成員值朱监,Redis 借助數(shù)據(jù)區(qū)間的表示方法岸啡,“[”表示包含該值,“(”表示不包含該值 |
zrange key start stop [withscores] | 按照分值的大泻毡唷(從小到大)返回成員巡蘸,加入 start 和 stop 參數(shù)可以截取某一段返回。如果輸入可選項(xiàng) withscores擂送,則連同分?jǐn)?shù)一起返回 | 這里記集合最人長(zhǎng)度為 len悦荒,則 Redis 會(huì)將集合排序后,形成一個(gè)從 0 到 len-1 的下標(biāo)逾冬,然后根據(jù) start 和 stop 控制的下標(biāo)(包含 start 和 stop)返回 |
zrank key member | 按從小到大求有序集合的排行 | 排名第一的為 0,第二的為 1…… |
zrangebylex key min max [limit offset count] | 根據(jù)值的大小躺苦,從小到大排序身腻,min 為最小值,max 為最大值匹厘;limit 選項(xiàng)可選嘀趟,當(dāng) Redis 求出范圍集合后,會(huì)生產(chǎn)下標(biāo) 0 到 n愈诚,然后根據(jù)偏移量 offset 和限定返回?cái)?shù) count她按,返回對(duì)應(yīng)的成員 | 這里范圍為 key 的成員值,Redis 借助數(shù)學(xué)區(qū)間的表示方法炕柔,“[”表示包含該值酌泰,“(”表示不包含該值 |
zrangebyscore key min max [withscores] [limit offset count] | 根據(jù)分?jǐn)?shù)大小,從小到大求取范圍匕累,選項(xiàng) withscores 和 limit 請(qǐng)參考 zrange 命令和 zrangebylex 說(shuō)明 | 根據(jù)分析求取集合的范圍陵刹。這里默認(rèn)包含 min 和 max,如果不想包含欢嘿,則在參數(shù)前加入“(”衰琐, 注意不支持“[”表示 |
zremrangebyscore key start stop | 根據(jù)分?jǐn)?shù)區(qū)間進(jìn)行刪除 | 按照 socre 進(jìn)行排序也糊,然后排除 0 到 len-1 的下標(biāo),然后根據(jù) start 和 stop 進(jìn)行刪除羡宙,Redis 借助數(shù)學(xué)區(qū)間的表示方法狸剃,“[”表示包含該值,“(” 表示不包含該值 |
zremrangebyrank key start stop | 按照分?jǐn)?shù)排行從小到大的排序刪除狗热,從 0 開始計(jì)算 | — |
zremrangebylex key min max | 按照值的分布進(jìn)行刪除 | — |
zrevrange key start stop [withscores] | 從大到小的按分?jǐn)?shù)排序钞馁,參數(shù)請(qǐng)參見(jiàn) zrange | 與 zrange 相同,只是排序是從大到小 |
zrevrangebyscore key max min [withscores] | 從大到小的按分?jǐn)?shù)排序匿刮,參數(shù)請(qǐng)參見(jiàn) zrangebyscore | 與 zrangebyscore 相同僧凰,只是排序是從大到小 |
zrevrank key member | 按從大到小的順序,求元素的排行 | 排名第一位 0僻焚,第二位 1...... |
zscore key member | 返回成員的分?jǐn)?shù)值 | 返回成員的分?jǐn)?shù) |
zunionstore desKey numKeys key1 [key2 key3 key4......] | 求多個(gè)有序集合的并集允悦,其中 numKeys 是有序集合的個(gè)數(shù) | —— |
在對(duì)有序集合膝擂、下標(biāo)虑啤、區(qū)間的表示方法進(jìn)行操作的時(shí)候,需要十分小心命令架馋,注意它是操作分?jǐn)?shù)還是值狞山,稍有不慎就會(huì)出現(xiàn)問(wèn)題。
這里命令比較多叉寂,也有些命令比較難使用萍启,在使用的時(shí)候,務(wù)必要小心屏鳍,不過(guò)好在我們使用 zset 的頻率并不是太高勘纯,下面是測(cè)試結(jié)果——有序集合命令展示,如圖所示钓瞭。
圖 2 有序集合命令展示
出演示的例子驳遵。測(cè)試代碼如下所示。
<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="java" cid="n515" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> public static void testZset() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"applicationContext.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
// Spring提供接口 TypedTuple操作有序集合
Set<TypedTuple> set1 = new HashSet<TypedTuple>();
Set<TypedTuple> set2 = new HashSet<TypedTuple>();
int j = 9;
for (int i = 1; i <= 9; i++) {
j--;
// 計(jì)算分?jǐn)?shù)和值
Double score1 = Double.valueOf(i);
String value1 = "x" + i;
Double score2 = Double.valueOf(j);
String value2 = j % 2 == 1 ? "y" + j : "x" + j;
// 使用 Spring 提供的默認(rèn) TypedTuple--DefaultTypedTuple
TypedTuple typedTuple1 = new DefaultTypedTuple(value1, score1);
set1.add(typedTuple1);
TypedTuple typedTuple2 = new DefaultTypedTuple(value2, score2);
set2.add(typedTuple2);
}
// 將元素插入有序集合zset1
redisTemplate.opsForZSet().add("zset1", set1);
redisTemplate.opsForZSet().add("zset2", set2);
// 統(tǒng)計(jì)總數(shù)
Long size = null;
size = redisTemplate.opsForZSet().zCard("set1");
// 計(jì)分?jǐn)?shù)為score山涡,那么下面的方法就是求 3<=score<=6的元素
size = redisTemplate.opsForZSet().count("zset1", 3, 6);
Set set = null;
// 從下標(biāo)一開始截取5個(gè)元素堤结,但是不返回分?jǐn)?shù),每一個(gè)元索是String
set = redisTemplate.opsForZSet().range("zset1", 1, 5);
printSet(set);
// 截取集合所有元素鸭丛,并且對(duì)集合按分?jǐn)?shù)排序竞穷,并返回分?jǐn)?shù),每一個(gè)元素是TypedTuple
set = redisTemplate.opsForZSet().rangeWithScores("zset1", 0, -1);
printTypedTuple(set);
// 將zset1和zset2兩個(gè)集合的交集放入集合inter_zset
size = redisTemplate.opsForZSet().intersectAndStore("zset1", "zset2","inter_zset");
// 區(qū)間
Range range = Range.range();
range.lt("x8");// 小于
range.gt("x1"); // 大于
set = redisTemplate.opsForZSet().rangeByLex("zset1", range);
printSet(set);
range.lte("x8"); // 小于等于
range.gte("xl"); // 大于等于
set = redisTemplate.opsForZSet().rangeByLex("zset1", range);
printSet(set);
// 限制返回個(gè)數(shù)
Limit limit = Limit.limit();
// 限制返回個(gè)數(shù)
limit.count(4);
// 限制從第五個(gè)開始截取
limit.offset(5);
// 求區(qū)間內(nèi)的元素鳞溉,并限制返回4條
set = redisTemplate.opsForZSet().rangeByLex("zset1", range, limit);
printSet(set);
// 求排行瘾带,排名第1返回0,第2返回1
Long rank = redisTemplate.opsForZSet().rank("zset1", "x4");
System.err.println("rank = " + rank);
// 刪除元素熟菲,返回刪除個(gè)數(shù)
size = redisTemplate.opsForZSet().remove("zset1", "x5", "x6");
System.err.println("delete = " + size);
// 按照排行刪除從0開始算起月弛,這里將刪除第排名第2和第3的元素
size = redisTemplate.opsForZSet().removeRange("zset2", 1, 2);
// 獲取所有集合的元素和分?jǐn)?shù)肴盏,以-1代表全部元素
set = redisTemplate.opsForZSet().rangeWithScores("zset2", 0, -1);
printTypedTuple(set);
// 刪除指定的元素
size = redisTemplate.opsForZSet().remove("zset2", "y5", "y3");
System.err.println(size);
// 給集合中的一個(gè)元素的分?jǐn)?shù)加上11
Double dbl = redisTemplate.opsForZSet().incrementScore("zset1", "x1",11);
redisTemplate.opsForZSet().removeRangeByScore("zset1", 1, 2);
set = redisTemplate.opsForZSet().reverseRangeWithScores("zset2", 1, 10);
printTypedTuple(set);
}
?
/**
- 打印TypedTuple集合
- @param set
- -- Set<TypedTuple>
*/
public static void printTypedTuple(Set<TypedTuple> set) {
if (set != null && set.isEmpty()) {
return;
}
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
TypedTuple val = (TypedTuple) iterator.next();
System.err.print("{value = " + val.getValue() + ", score = "
- val.getScore() + "}\n");
}
}
?
/**
- 打印普通集合
- @param set普通集合
*/
public static void printSet(Set set) {
if (set != null && set.isEmpty()) {
return;
}
Iterator iterator = set.iterator();
while (iterator .hasNext()) {
Object val = iterator.next();
System. out.print (val +"\t");
}
System.out.println();
}</pre>
Redis HyperLogLog常用命令
基數(shù)是一種算法。舉個(gè)例子帽衙,一本英文著作由數(shù)百萬(wàn)個(gè)單詞組成菜皂,你的內(nèi)存卻不足以存儲(chǔ)它們。
英文單詞本身是有限的厉萝,在這本書的幾百萬(wàn)個(gè)單詞中有許許多多重復(fù)單詞恍飘,扣去重復(fù)的單詞,這本書中也就是幾千到一萬(wàn)多個(gè)單詞而已谴垫,那么內(nèi)存就足夠存儲(chǔ)它章母。
比如數(shù)字集合 {1,2,5,7,9,1,5,9} 的基數(shù)集合為 {1,2,5,7,9} 那么基數(shù)(不重復(fù)元素)就是 5,基數(shù)的作用是評(píng)估大約需要準(zhǔn)備多少個(gè)存儲(chǔ)單元去存儲(chǔ)數(shù)據(jù)翩剪,但是基數(shù)的算法一般會(huì)存在一定的誤差(一般是可控的)乳怎。Redis 對(duì)基數(shù)數(shù)據(jù)結(jié)構(gòu)的支持是從版本 2.8.9 開始的。
基數(shù)并不是存儲(chǔ)元素前弯,存儲(chǔ)元素消耗內(nèi)存空間比較大蚪缀,而是給某一個(gè)有重復(fù)元素的數(shù)據(jù)集合(一般是很大的數(shù)據(jù)集合)評(píng)估需要的空間單元數(shù),所以它沒(méi)有辦法進(jìn)行存儲(chǔ)恕出,加上在工作中用得不多询枚,所以簡(jiǎn)要介紹一下 Redis 的 HyperLogLog 命令就可以了,如表所示浙巫。
命 令 | 說(shuō) 明 | 備 注 |
---|---|---|
pfadd key element | 添加指定元素到 HyperLogLog 中 | 如果已經(jīng)存儲(chǔ)元索金蜀,則返回為 0,添加失敗 |
pfcount key | 返回 HyperLogLog 的基數(shù)值 | — |
pfmerge desKey key1 [key2 key3...] | 合并多個(gè) HyperLogLog的畴,并將其保存在 desKey 中 | — |
在命令行中演示一下它們渊抄,如圖 1 所示。
圖 1 Redis 的 HyperLogLog 命令演示
分析一下邏輯丧裁,首先往一個(gè)鍵為 h1 的 HyperLogLog 插入元素护桦,讓其計(jì)算基數(shù),到了第 5 個(gè)命令“pfadd h1 a”的時(shí)候渣慕,由于在此以前已經(jīng)添加過(guò)嘶炭,所以返回了 0。它的基數(shù)集合是 {a,b,c,d}逊桦,故而求集合長(zhǎng)度為 4眨猎;之后再添加了第二個(gè)基數(shù),它的基數(shù)集合是{a,z}强经,所以在 h1 和 h2 合并為 h3 的時(shí)候睡陪,它的基數(shù)集合為 {a,b,c,d,z},所以求取它的基數(shù)就是 5。
在 Spring 中操作基數(shù)兰迫,代碼如下所示信殊。
<pre spellcheck="false" class="md-fences mock-cm md-end-block md-fences-with-lineno" lang="java" cid="n544" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: pre-wrap; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 8px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">ApplicationContext applicationContext = new ClassPathXmlApplicationcontext("applicationContext.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
redisTemplate.opsForHyperLogLog().add("HyperLogLog", "a", "b" , "c", "d", "a");
redisTemplate.opsForHyperLogLog().add("HyperLogLog2", "a"); redisTemplate.opsForHyperLogLog().add("HyperLogLog2", "z");
Long size = redisTemplate.opsForHyperLogLog().size("HyperLogLog");
System.err.println(size);
size = redisTemplate.opsForHyperLogLog().size("HyperLogLog2");
System.err.println(size);
redisTemplate.opsForHyperLogLog().union ("des_key","HyperLogLog","HyperLogLog2");
size = redisTemplate.opsForHyperLogLog().size("des_key");
System.err.println(size);</pre>
從上面的代碼可以看到,增加一個(gè)元素到基數(shù)中采用 add 方法汁果,它可以是一個(gè)或者多個(gè)元素涡拘,而求基數(shù)大小則是采用了 size 方法,合并基數(shù)則采用了 union 方法据德,其第一個(gè)是目標(biāo)基數(shù)的 key鳄乏,然后可以是一到多個(gè) key。