網站首頁 編程語言 正文
Redis - String內存開銷問題以及基本/擴展數據類型的使用
- 一. String 類型內存開銷問題
- 1.1 SDS 結構
- 1.2 RedisObject 結構
- 1.3 String 類型的內存布局優化
- 1.4 壓縮列表的優勢
- 二. Redis 基本操作和擴展集合的使用
- 2.1 基于 Redis 和 Java 的基本操作
- 2.1.1 String
- 2.1.1 List
- 2.1.1 Hash
- 2.1.1 Set
- 2.1.1 Sorted Set
- 2.2 擴展集合的使用
- 2.2.1 Bitmap 二值統計
- 2.2.2 HyperLogLog 基數統計
- 2.2.3 GEO 經緯度計算
一. String 類型內存開銷問題
Redis
中String
類型保存值的時候,有兩個選擇:
- 保存整數:采用
int
編碼,將其保存為一個8字節的類型整數。 - 保存字符:使用
SDS
結構保存。
可見,Redis
它本身并不傻,如果在String
類型中,存儲的值是一個整數,它會自動進行int
編碼。而保存的數據中若包含了字符,即采用簡單動態字符串SDS
結構體來保存。
1.1 SDS 結構
SDS
,即Simple Dynamic String
。低版本的Redis SDS
結構如圖(Redis3.2
版本以下):
總共有三個部分:
-
len
:表示buf
的已用長度。占4個字節。 -
alloc
:表示buf
的時機分配長度,占4個字節。 -
buf
:字節數組,保存真實的數據。Redis
會在這個數組的末尾自動加一個"\0"
,代表結束標識。額外占用1個字節的開銷。
1.2 RedisObject 結構
對于Redis
的String
類型而言,還涉及到RedisObject
結構。該結構體主要用于記錄一些元數據記錄(最后一次的訪問時間,被引用的次數等等)包含了8個字節大小的元數據和8個字節大小的指針,共16字節。同時該結構體還指向實際的數據。如圖:
意思就是,每當往Redis
中插入一個String
類型的鍵值對后,就會構建出對于的SDS
結構(若是字符類型),以及一個額外的數據結構RedisObject
(存儲相關的元數據),并與之綁定。
1.3 String 類型的內存布局優化
到這里,我們可以知道,Redis
中對于String
類型的鍵值對存儲,這幾個部分可能是“多余”的:
-
SDS
中的len
以及alloc
。 -
RedisObject
中的元數據以及指針ptr
。
為了節省內存空間,實際上Redis
對Long
類型的整數以及SDS
的布局做了對應的優化:
- 倘若保存的是
Long
類型整數:RedisObject
中的指針就是整數本身,無需額外的指針去指向實際數據。 - 倘若保存的是字符串數據:當字符串<=44字節的時候,
RedisObject
中的元數據以及指針ptr
和SDS
是一塊連續的內存區域。即embstr
編碼。目的:避免內存碎片。 否則,當字符串>44字節的時候,將會給SDS
分配獨立的空間,并用上圖所示的方式,指針ptr
去指向SDS
結構,此時稱之為raw
編碼。
三種編碼方式,用圖所示如下:
我這里準備了一個Redis
服務器,首先看下它的占用內存是多少:
public static void main(String[] args) {
Jedis jedis = new Jedis("xxx", 6379);
//授權
jedis.auth("xxx");
System.out.println(jedis.info());
}
結果打印出來如下:看used_memory
,值為918704
。
倘若我此時插入一個key-Value
:
jedis.set("20", "30");
再看下結果:看used_memory
,值為918736
。
內存一共多了32B,分析如下:
-
Key
和Value
都是整數。因此Redis
會對其采用int
編碼。 -
int
編碼情況下,使用RedisObject
結構保存。其中元數據占8個字節,指針部分則由8字節的整數來代替。一共16字節。 - 因此
Key+Value
總共消耗的內存為32字節。
除此之外,我們還知道,Redis
用了一張全局的Hash
表來保存所有的鍵值對。哈希表中的每一項是一個哈希桶,哈希桶中又包含了多個dictEntry
的結構體,結構圖如下:dictEntry
一共占用了24個字節大小。但是同時,Redis
中有一個內存分配庫jemalloc
,當我們插入一個鍵值對的時候,會根據申請的字節數N
,找一個比N
大的最小二次冪作為分配的空間作為dictEntry
的大小。那么此時dictEntry
的大小就是固定的32字節。
也就是說,在假設Redis
中沒有任何數據的時候,執行set 20 20
時,一共會占用64內存大小。但實際上,真實的數據卻只有16字節。Key
和Value
各對應一個RedisObject
,其中的指針(由于是int
編碼,因此轉為整數本身)就是我們要的真實數據。
同時我們還應該注意到:我們對于String
類型的數據,每插入一條,就會對應的在全局哈希表中生成一個dictEntry
結構體,占用32字節的大小。倘若有1億條數據插入,就會生成1億個dictEntry
結構體。同時哈希桶還得不斷地擴容,保證大小為2的N次冪。
1.4 壓縮列表的優勢
假設:在Redis
中存儲大量的Key-Value
映射,比如set 用戶Id 會員Id
,然后用戶Id
和會員Id
都是唯一,并且數據量很大,從下述Id
處開始添加10000條數據。
set 11010001 12010001
偽代碼就是
for(int i=0; i <10000;i++){
set (11010001+ i) (12010001+i)
}
首先來說下倘若使用String類型來存儲的劣勢:
- 每插入一條數據,對于生成一個32B大小的
dictEntry
。 -
RedisObject
來存儲這樣的整形數據,雖然有int
編碼,但是還是有多余的元數據信息,占用8B。
那么這里可以采用壓縮列表來保存。壓縮列表的數據結構如圖:
-
zlbytes
:列表長度。 -
zltail
:列表尾的偏移量。 -
zllen
:列表中的entry
個數。 -
zlend
:表示列表結束。
entry
中的各個屬性:
-
prev_len
,表示前一個entry
的長度。要么1字節(上一個entry
的長度<254B)要么5字節。 -
len
:表示自身長度,4 個字節。 -
encoding
:表示編碼方式,1 個字節。 -
content
:保存實際數據。
因此對于本文的案例來說,存儲用戶Id
的時候,由于其字節大小不會超過254B,因此prev_len
的大小為1B。那么每個entry
的大小就是:1+4+1+8(Long
整形)=14個字節,然后根據內存分配器的原則,取最靠近的二次冪數16,即每個entry
大小為16字節。
而我們向同一個壓縮列表中添加數據的時候,只會改變壓縮列表內entry
的個數,而全局哈希表中,對于這個壓縮列表生成的dictEntry
對象個數卻不會增加,這是和String
類型存儲的一個重要區別。
測試如下,采用String
類型添加500條數據:
public static void main(String[] args) {
Jedis jedis = new Jedis("xxx", 6379);
//授權
jedis.auth("xxx");
String before = getMemorySize(jedis);
System.out.println("Before Size: " + before);
LongAdder key = new LongAdder();
LongAdder value = new LongAdder();
key.add(11010001);
value.add(12010001);
for (int i = 0; i < 500; i++) {
key.add(1);
value.add(1);
jedis.set(key.toString(), value.toString());
}
String after = getMemorySize(jedis);
System.out.println("After Size: " + after);
System.out.println(Integer.parseInt(after) - Integer.parseInt(before));
}
static String getMemorySize(Jedis jedis) {
String[] split = jedis.info().split("\r\n");
String msg = "";
for (String s : split) {
if (s.contains("used_memory")) {
msg = s;
break;
}
}
String[] res = msg.split(":");
return res[1];
}
結果如下:
倘若改成壓縮列表:用戶Id
為11010001,我們取前五位作為壓縮列表的鍵,然后后三位作為其key
,會員Id
作為value
。代碼:
public static void main(String[] args) {
Jedis jedis = new Jedis("xxx", 6379);
//授權
jedis.auth("xxx");
String before = getMemorySize(jedis);
System.out.println("Before Size: " + before);
LongAdder key = new LongAdder();
LongAdder value = new LongAdder();
key.add(11010001);
value.add(12010001);
for (int i = 0; i < 500; i++) {
key.add(1);
value.add(1);
// 壓縮列表的key
String hashKey = key.toString().substring(0, 5);
// 集合內部每個entry的value
String listValue = value.toString();
// 集合內部每個entry的key
String listKey = key.toString().substring(5, 7);
jedis.hset(hashKey, listKey, listValue);
}
String after = getMemorySize(jedis);
System.out.println("After Size: " + after);
System.out.println(Integer.parseInt(after) - Integer.parseInt(before));
}
static String getMemorySize(Jedis jedis) {
String[] split = jedis.info().split("\r\n");
String msg = "";
for (String s : split) {
if (s.contains("used_memory")) {
msg = s;
break;
}
}
String[] res = msg.split(":");
return res[1];
}
結果如下:
可見,壓縮列表的使用,在這種場景下,比單純的使用String
類型,在內存消耗上要節省的多的多。
不過有一點需要注意的是,Redis
中Hash
類型的底層數據結構有兩種:壓縮列表和哈希表。倘若數據超過一定的閾值,就會改用哈希表來存儲,此時數據結構就并不像壓縮列表那樣緊湊了。相關的閾值涉及到兩個:
-
hash-max-ziplist-entries
:表示用壓縮列表保存時哈希集合中的最大元素個數。 -
hash-max-ziplist-value
:表示用壓縮列表保存時哈希集合中單個元素的最大長度。
我們取的是用戶Id的后三位作為壓縮列表的key,也就是說這個壓縮列表中的數據個數不超過1000個。為了能充分使用壓縮列表的精簡內存布局,我們一般要控制保存在 Hash
集合中的元素個數。因此我們可以將 hash-max-ziplist-entries
的值設置為1000。 這樣Hash
集合就可以使用壓縮列表來節省空間了。
到這里為止講了什么內容?
- 在面對這種有一定規則(比如單調遞增的
Id
),并且在Redis
中存儲的情況下,壓縮列表比單純的使用String
類型一條條存儲,在內存開銷上,要少的多。 - 還講了
String
類型在存儲的時候,具體的內存消耗在哪些地方了。 -
Redis
高低版本中,關于SDS
的結構以及其他數據結構可能會有所不同,因此在計算插入一個鍵值對的時候,計算內存大小前后可能會有所差異。
二. Redis 基本操作和擴展集合的使用
Redis
中有5個基本數據類型:String、List、Hash、Set、Sorted Set
。
2.1 基于 Redis 和 Java 的基本操作
首先是Java
的pom
依賴:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.1.1</version>
</dependency>
接下來就給出Redis
中關于基本數據類型的幾種常見的命令操作。
2.1.1 String
Redis
相關操作:
# 指定key對應的值
SET key value
# 獲取指定 key 的值
GET key
# 返回 key 中字符串值的子字符,即字符串的截取,包括start和end所在的位置
GETRANGE key start end
# 獲取多個給定 key 的值
MGET key1 [key2..]
# 將值 value 關聯到 key ,并將 key 的過期時間設為 seconds (以秒為單位)
SETEX key seconds value
# 只有在 key 不存在時設置 key 的值
SETNX key value
# 返回 key 所儲存的字符串值的長度
STRLEN key
# 批量設置k-v
MSET key value [key value ...]
# 將 key 中儲存的數字值增一
INCR key
# 將 key 所儲存的值加上給定的增量值(increment)
INCRBY key increment
# 將 key 中儲存的數字值減一
DECR key
Java
相關操作:
// 向redis中添加數據
jedis.set("k1", "v1");
jedis.set("k2", "k2");
jedis.set("k3", "k3");
jedis.set("k4", "k4");
jedis.set("k5", "k5");
// 查看某個鍵對應值的數據類型
String type = jedis.type("k1");
// 獲取redis中全部的key
Set<String> keys = jedis.keys("*");
// 刪除redis中的一個鍵值對
Long del = jedis.del("name");
// 判斷是否存在指定的key
Boolean k1 = jedis.exists("k1");
// 判斷指定的key的過期時間
Long k11 = jedis.ttl("k1");
// 向redis中添加多個key-value
String mSet = jedis.mset("test1", "111", "test2", "222", "test3", "333");
// 獲取redis中的多個key-value
List<String> mGet = jedis.mget("test1", "test2", "test3");
// 給redis中指定的key對應的值加一,可以加第二個參數,指定加/減多少數值,否則默認1
Long incr = jedis.incr("presence");
// 給redis中指定的key對應的值減一
Long decr = jedis.decr("presence");
// 清空redis中的數據
String s = jedis.flushDB();
2.1.1 List
Redis
相關操作:
# 通過索引獲取列表中的元素
LINDEX key index
# 獲取列表長度
LLEN key
# 移出并獲取列表的第一個元素
LPOP key
# 將一個或多個值插入到列表頭部
LPUSH key value1 [value2]
# 獲取列表指定范圍內的元素
LRANGE key start stop
# 移除列表元素
LREM key count value
# 通過索引設置列表元素的值
LSET key index value
# 移除列表的最后一個元素,返回值為移除的元素
RPOP key
# 在列表尾部中添加一個或多個值
RPUSH key value1 [value2]
Java
相關操作:
// 向redis中從左邊添加一個集合數據
System.out.println("添加元素a,b,c,d");
Long lPush = jedis.lpush("list1", "a", "b", "c", "d");
// 查看list1集合中元素的個數
System.out.println("list1集合中元素的個數:" + jedis.llen("list1"));
// 從左邊獲取redis中的list1集合數據
List<String> list1 = jedis.lrange("list1", 0, -1);
System.out.println("獲取集合數據:" + list1);
// 從list1集合的左邊彈出一個元素
String lPop = jedis.lpop("list1");
System.out.println("list1集合從左邊彈出一個元素:" + lPop);
// 從list1集合的右邊彈出一個元素
String rPop = jedis.rpop("list1");
System.out.println("list1集合從右邊彈出一個元素:" + rPop);
// 查看list1集合中元素的個數
System.out.println("list1集合中元素的個數:" + jedis.llen("list1"));
// 向redis中從右邊添加一個集合數據
Long rPush = jedis.rpush("list2", "a", "b", "c", "d");
// 查看list2集合中元素的個數
Long list2 = jedis.llen("list2");
// 設置下標為1的元素值為666
jedis.lset("list2", 1, "666");
List<String> list3 = jedis.lrange("list2", 0, -1);
System.out.println("獲取集合數據:" + list3);
System.out.println("***********清空redis數據***********");
//清空redis中的數據
String s = jedis.flushDB();
System.out.println(s);
結果如下:
2.1.1 Hash
Redis
相關操作:
# 刪除一個或多個哈希表字段
HDEL key field1 [field2]
# 查看哈希表 key 中,指定的字段是否存在
HEXISTS key field
# 獲取存儲在哈希表中指定字段的值
HGET key field
# 獲取在哈希表中指定 key 的所有字段以及和
HGETALL key
# 獲取所有哈希表中的字段
HKEYS key
# 獲取哈希表中字段的數量
HLEN key
# 批量獲取所有給定字段的值
HMGET key field1 [field2]
# 批量設置
HMSET key field1 value1 [field2 value2 ]
# 將哈希表 key 中的字段 field 的值設為 value
HSET key field value
# 獲取哈希表中所有值
HVALS key
Java
相關操作:
System.out.println("***********向redis中添加hash類型數據***********");
// 向redis中添加hash類型數據(添加一個數據)
Long hSet = jedis.hset("student1", "name", "李四");
System.out.println("添加一個數據: name-李四");
// 獲取添加的一個數據
String name = jedis.hget("student1", "name");
System.out.println("獲取name指定的數據: " + name);
// 向redis中添加hash類型數據(添加多個數據)
Map<String, String> map = new HashMap<>();
map.put("name", "張三");
map.put("sex", "男");
map.put("address", "上海");
String msg = jedis.hmset("student2", map);
System.out.println("添加多個數據: " + msg);
// 獲取多個數據
List<String> hmGet = jedis.hmget("student2", "name", "sex", "address");
System.out.println("獲取多個數據: " + hmGet);
Long len = jedis.hlen("student2");
System.out.println("哈希表中字段數量:" + len);
System.out.println("***********清空redis數據***********");
// 清空redis中的數據
String s = jedis.flushDB();
System.out.println(s);
結果如下:
2.1.1 Set
Redis
相關操作:
# 向集合添加一個或多個成員
SADD key member1 [member2]
# 獲取集合的成員數
SCARD key
# 返回第一個集合與其他集合之間的差異
SDIFF key1 [key2]
# 返回給定所有集合的交集
SINTER key1 [key2]
# 判斷 member 元素是否是集合 key 的成員
SISMEMBER key member
# 返回集合中的所有成員
SMEMBERS key
# 移除集合中一個或多個成員
SREM key member1 [member2]
# 返回所有給定集合的并集
SUNION key1 [key2]
Java
相關操作:
// 向redis中添加Set集合數據
Long sAdd = jedis.sadd("set1", "a", "b", "c", "d", "d", "e", "f");
// 獲取redis中的set1集合數據
Set<String> set1 = jedis.smembers("set1");
System.out.println("獲取Set集合數據:" + set1);
// 刪除redis中set1集合中指定的數據
Long sRem = jedis.srem("set1", "a", "b");
System.out.println("刪除Set集合數據a,b");
System.out.println("獲取Set集合數據:" + jedis.smembers("set1"));
System.out.println("***********Set交并差運算***********");
System.out.println("set1: a,b,c,d,e,f");
System.out.println("set2: a,b,g,h,i");
Long sadd1 = jedis.sadd("set1", "a", "b", "c", "d", "e", "f");
Long sadd2 = jedis.sadd("set2", "a", "b", "g", "h", "i");
//獲取交集
Set<String> sinter = jedis.sinter("set1", "set2");
System.out.println("交集:" + sinter);
//獲取并集
Set<String> sunion = jedis.sunion("set1", "set2");
System.out.println("并集:" + sunion);
//獲取差集
Set<String> sdiff = jedis.sdiff("set1", "set2");
System.out.println("差集:" + sdiff);
System.out.println("***********清空redis數據***********");
//清空redis中的數據
String s = jedis.flushDB();
System.out.println(s);
結果如下:
2.1.1 Sorted Set
Redis
相關操作:
# 向有序集合添加一個或多個成員
ZADD key score1 member1 [score2 member2]
# 獲取有序集合的成員數
ZCARD key
# 計算在有序集合中指定區間分數的成員數
ZCOUNT key min max
# 有序集合中對指定成員的分數加上增量
ZINCRBY key increment member
# 移除有序集合中的一個或多個成員
ZREM key member [member ...]
# 移除有序集合中給定的字典區間的所有成員
ZREMRANGEBYLEX key min max
# 返回有序集中指定區間內的成員,通過索引,分數從高到低
ZREVRANGE key start stop [WITHSCORES]
# 返回有序集中,成員的分數值
ZSCORE key member
Java
相關操作:由于主要還是用于排序,我感覺用的還是比較少的。就給個簡單的用例吧。
jedis.zadd("city", 10d, "上海");
jedis.zadd("city", 30d, "溫州");
jedis.zadd("city", 20d, "北京");
Set<String> city = jedis.zrange("city", 0, -1);
System.out.println("排序:" + city);
結果如下:
2.2 擴展集合的使用
此外Redis
還有3種擴展數據類型,一般用于海量數據的統計以及特殊場景。有:Bitmap
、HyperLogLog
和 GEO
。
2.2.1 Bitmap 二值統計
首先來說下什么是二值統計:集合元素的取值只有0和1兩種。那么典型的運用場景就是打卡簽到了:
- 打卡了–>1。
- 未打卡–>0。
首先來大概講一下Bitmap
,Bitmap
本身利用String
類型作為底層的數據結構。可以保存二進制的字節數組,因此將字節數組的每個bit
位代表一個元素的二值狀態。Bitmap
就相當于一個bit
數組。
Redis
操作:
SETBIT user.01.202208 0 1 ;
SETBIT user.01.202208 2 1 ;
SETBIT user.01.202208 7 1 ;
# 統計
BITCOUNT user.01.202208
那么代碼怎么去編寫呢?
public static void main(String[] args) {
Jedis jedis = new Jedis("xxx", 6379);
//授權
jedis.auth("xxx");
String key = "user.01.202208";
jedis.setbit(key, 0, true);
jedis.setbit(key, 2, true);
jedis.setbit(key, 3, true);
jedis.setbit(key, 7, true);
System.out.println(jedis.getbit(key,0));
System.out.println(jedis.getbit(key,3));
System.out.println(jedis.getbit(key,5));
}
結果如下:
簡單來說就是:
- 該
Bitmap
記錄的是:用戶編號為01,在2022年8月份的打卡記錄。 - 用戶在1號,3號,4號18號打了卡。因此
jedis.getbit(key,5)
的時候,是沒打卡的,返回false
。當然,你可以從1開始設置。只不過Bitmap
的偏移量從0開始算。不過不影響。 - 那么這個月的打卡記錄只要統計這個月中值為1的個數就可以了。如果在Redis中,可以命令操作:
bitcount user.01.202208
。
2.2.2 HyperLogLog 基數統計
先來給個應用場景:基數統計。即統計一個集合中不重復的元素。
如果是Set
的使用,Redis
命令:
sadd key value;
sadd key value2;
# 統計命令
scard key
如果是Java
:
jedis.sadd("mySet","123");
jedis.sadd("mySet","1234");
jedis.sadd("mySet","1235");
jedis.sadd("mySet","123");
Set<String> mySet = jedis.smembers("mySet");
System.out.println(mySet.size());
如果是Hash
的使用,Redis
命令:
hset test user1 1;
hset test user2 2;
# 統計命令
hlen test
Java
命令:
edis.hset("myhash","username","lcg");
jedis.hset("myhash","username","lcg1");
Map<String, String> myhash = jedis.hgetAll("myhash");
System.out.println(myhash.size());
但是這兩種情況,在數據量非常大的情況下,Set
和Hash
類型都會消耗很大的內存空間。因此這里可以使用HyperLogLog
來替代,HyperLogLog
專門用來處理這種海量數據的基數操作。
HyperLogLog
其優勢在于當集合元素數量非常多時,它計算基數所需的空間總是固定的,而且還很小。在 Redis
中,每個 HyperLogLog
只需要花費 12 KB
內存,就可以計算接近 2^64 個元素的基數。
例如,統計某個頁面中,訪問的用戶有哪些:
pfadd page user1 user2 user3 user4 user5;
# 統計
pfcount page;
HyperLogLog
有一點需要值得注意,HyperLogLog
雖然快,但是犧牲了一定的統計準確度:HyperLogLog
的統計規則是基于概率完成的,所以統計結果有一定誤差,標準誤算率是 0.81%。
2.2.3 GEO 經緯度計算
GEO
主要涉及到經緯度的一個計算,例如車輛的定位信息,假設車輛 ID
是 666,經緯度位置是(120,40),我們可以用一個 GEO
集合保存所有車輛的經緯度,集合 key
是 location
。
GEOADD location 120 40 666
那么當我們需要統計這個坐標附近的車輛信息時候,我們就可以使用以下命令:
# 在經緯度120°,40°附近10km范圍內,尋找最近的10輛車輛。
GEORADIUS location 120 40 10 km ASC COUNT 10
其他的關于GEO
的Redis
操作如下:
# 添加地理位置的坐標
geoadd key 經度 緯度 menber
# 獲取地理位置的坐標
geopos key member [memberN....]
# 計算兩個位置之間的距離
geodist key member1 member2 [m|km|ft|mi]
# 根據用戶給定的經緯度坐標來獲取指定范圍內的地理位置集合
georadius location 經度 緯度 舉例 [m|km|ft|mi] [ASC|DESC] COUNT 10
Java
相關操作:
Jedis jedis = new Jedis("124.220.208.165", 6379);
//授權
jedis.auth("Ljj000..");
// 添加北京坐標信息
Long beijing = jedis.geoadd("china:city", 116.46, 39.92, "beijing");
System.out.println("添加北京坐標信息:" + beijing);
// 添加上海坐標信息
Long shanghai = jedis.geoadd("china:city", 121.48, 31.22, "shanghai");
System.out.println("添加上海坐標信息:" + shanghai);
// 添加杭州坐標信息
Long hangzhou = jedis.geoadd("china:city", 120.19, 30.26, "hangzhou");
System.out.println("添加杭州坐標信息:" + hangzhou);
// 獲取北京的坐標信息
List<GeoCoordinate> geoCoordinate = jedis.geopos("china:city", "beijing");
System.out.println("獲取北京的坐標信息:" + geoCoordinate);
// 獲取多個坐標信息
List<GeoCoordinate> geoCoordinates = jedis.geopos("china:city", "beijing", "shanghai");
System.out.println("獲取多個坐標信息:" + geoCoordinates);
// 獲取北京到上海的直線距離
Double distance = jedis.geodist("china:city", "beijing", "shanghai", GeoUnit.KM);
System.out.println("獲取北京到上海的直線距離(單位:KM):" + distance);
// 獲取距離指定點位距離的城市
List<GeoRadiusResponse> citys = jedis.georadiusByMember("china:city", "beijing", 1500, GeoUnit.KM);
System.out.println("獲取距離指定點位距離的城市:");
for (GeoRadiusResponse city:citys) {
System.out.print(city.getMemberByString() + "\t");
}
System.out.println();
// 獲取指定經緯度多少距離以內的元素
List<GeoRadiusResponse> geo1 = jedis.georadius("china:city", 116.46, 39.92, 1200, GeoUnit.KM);
System.out.println("獲取指定經緯度多少距離以內的元素: ");
for (GeoRadiusResponse city:geo1) {
System.out.print(city.getMemberByString() + "\t");
}
System.out.println();
System.out.println("***********清空redis中的數據***********");
//清空redis中的數據
String s = jedis.flushDB();
結果如下:
原文鏈接:https://blog.csdn.net/Zong_0915/article/details/126157840
相關推薦
- 2022-04-08 swift表格控件使用方法詳解(UITableview)_Swift
- 2022-12-22 C語言如何使用函數求素數和舉例_C 語言
- 2022-06-12 .Net?Core?3.1?Web?API基礎知識詳解(收藏)_實用技巧
- 2022-07-30 Linux下查看文件和文件夾大小
- 2022-02-27 Postgres -- 報錯:right sibling‘s left-link doesn‘t m
- 2022-05-12 tp6 app接口集成Swagger生成接口文檔
- 2022-06-07 SQL?Server內存機制詳解_MsSql
- 2022-05-14 c++與python實現二分查找的原理及實現_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支