2020重新出發(fā)京办,NOSQL待侵,Redis的常用數(shù)據(jù)類型

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 所示矾利。

Redis字符串?dāng)?shù)據(jù)結(jié)構(gòu)

圖 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 所示锯茄。

Redis操作字符串重用命令

圖 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é)果如下:

運(yùn)行結(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 所示。

操作浮點(diǎn)數(shù)和整數(shù)

圖 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 所示琢锋。

Redis的hash結(jié)構(gòu)命令展示

圖 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)容:

  1. hmset 命令,在 Java 的 API 中寇甸,是使用 map 保存多個(gè)鍵值對(duì)在先的塘偎。

  2. hgetall 命令會(huì)返回所有的鍵值對(duì),并保存到一個(gè) map 對(duì)象中拿霉,如果 hash 結(jié)構(gòu)很大式塌,那么要考慮它對(duì) JVM 的內(nèi)存影響。

  3. hincrby 和 hincrbyFloat 命令都采用 increment 方法友浸,Spring 會(huì)識(shí)別它具體使用何種方法峰尝。

  4. redisTemplate.opsForHash().values(key) 方法相當(dāng)于 hvals 命令,它會(huì)返回所有的值收恢,并保存到一個(gè) List 對(duì)象中武学;而 redisTemplate.opsForHash().keys(key) 方法相當(dāng)于 hkeys 命令祭往,它會(huì)獲取所有的鍵,保存到一個(gè) Set 對(duì)象中火窒。

  5. 在 Spring 中使用 redisTemplate.opsForHash().putAll(key,map) 方法相當(dāng)于執(zhí)行了 hmset 命令硼补,使用了 map,由于配置了默認(rèn)的序列化器為字符串熏矿,所以它也只會(huì)用字符串進(jìn)行轉(zhuǎn)化已骇,這樣才能執(zhí)行對(duì)應(yīng)的數(shù)值加法,如果使用其他序列化器票编,則后面的命令可能會(huì)拋出異常褪储。

  6. 在使用大的 hash 結(jié)構(gòu)時(shí),需要考慮返回?cái)?shù)據(jù)的大小慧域,以避免返回太多的數(shù)據(jù)鲤竹,引發(fā) JVM 內(nèi)存溢出或者 Redis 的性能問(wèn)題。

運(yùn)行以上代碼昔榴,可以得到這樣的輸出結(jié)果:

運(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 所示。

鏈表結(jié)構(gòu)

圖 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 所示丈咐。

Redis關(guān)于鏈表的操作命令

圖 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 所示飒泻。

Redis鏈表阻塞操作命令

圖 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 所示催蝗。

通過(guò)命令行客戶端演示這些命令

圖 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 所示。

有序集合的數(shù)據(jù)結(jié)構(gòu)

圖 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 所示。

Redis的HyperLogLog命令演示

圖 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。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末棘利,一起剝皮案震驚了整個(gè)濱河市橱野,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌善玫,老刑警劉巖水援,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異茅郎,居然都是意外死亡蜗元,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門只洒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)许帐,“玉大人劳坑,你說(shuō)我怎么就攤上這事毕谴。” “怎么了距芬?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵涝开,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我框仔,道長(zhǎng)舀武,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任离斩,我火速辦了婚禮银舱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘跛梗。我一直安慰自己寻馏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布核偿。 她就那樣靜靜地躺著诚欠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上轰绵,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天粉寞,我揣著相機(jī)與錄音,去河邊找鬼左腔。 笑死唧垦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的液样。 我是一名探鬼主播业崖,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蓄愁!你這毒婦竟也來(lái)了双炕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤撮抓,失蹤者是張志新(化名)和其女友劉穎妇斤,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丹拯,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡站超,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乖酬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片死相。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖咬像,靈堂內(nèi)的尸體忽然破棺而出算撮,到底是詐尸還是另有隱情,我是刑警寧澤县昂,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布肮柜,位于F島的核電站,受9級(jí)特大地震影響倒彰,放射性物質(zhì)發(fā)生泄漏审洞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一待讳、第九天 我趴在偏房一處隱蔽的房頂上張望芒澜。 院中可真熱鬧,春花似錦创淡、人聲如沸痴晦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)阅酪。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間术辐,已是汗流浹背砚尽。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辉词,地道東北人必孤。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瑞躺,于是被迫代替她去往敵國(guó)和親敷搪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355