字典序的第K小數字--java
Redis 資料結構
目錄Redis 多資料庫
-
Redis 伺服器擁有16 個數據庫(0~15),預設使用 0 號資料庫,可以使用 select 命令來切換資料庫
注:16 個數據庫之間是完成獨立的,可以儲存相同的鍵值對
-
清空當前資料庫的資料,可以使用 flushdb 命令
-
清空 Redis 所有資料庫的資料 ,可以使用flushall
-
Redis不支援自定義資料庫名字,預設是: 0、1、2、3、4 ... 15
-
Redis不支援為每個資料庫設定不同的訪問密碼(預設密碼為空)
Redis 基本操作命令
注:Redis 的 key 只能是字串型別
-
set:插入資料
-
get:查詢資料
-
set:修改資料
-
del:刪除資料
Redis 通用操作命令
-
keys:查詢所有 key
格式是keys pattern, pattern支援glob風格萬用字元格式:
?匹配一個字元
*
匹配任意字元[]
匹配中括號內的任一字元,可以用 - 來表示範圍\x 匹配字元x,用於轉義符號
-
exists: 判斷鍵值是否存在
-
type: 獲取鍵值的資料型別
-
rename:改名
-
renamenx: 改名(newKey不存在)
Redis核心物件
在Redis中有一個「核心的物件」叫做redisObject
,是用來表示所有的key和value的,用redisObject結構體來表示String、Hash、List、Set、ZSet
五種資料型別。
redisObject
的原始碼在redis.h
中,使用c語言寫的,感興趣的可以自行檢視,關於redisObject我這裡畫了一張圖,表示redisObject的結構如下所示:
在redisObject中「type表示屬於哪種資料型別,encoding表示該資料的儲存方式」
127.0.0.1:6379> set k1 234
OK
127.0.0.1:6379> set k2 3.200
OK
127.0.0.1:6379> type k1 #檢視資料型別
string
127.0.0.1:6379> type k2
string
127.0.0.1:6379> object encoding k1 #檢視底層儲存的資料結構
"int"
127.0.0.1:6379> object encoding k2
"embstr"
Redis 資料庫支援五種資料型別:
- strings(字串)
- list(字串列表)
- set(字串集合)
- zset(有序字串集合)
- hash(雜湊)
key 命名注意事項:
- key不要太長,儘量不要超過1024位元組,這不僅消耗記憶體,而且會降低查詢的效率;
- key也不要太短,太短的話,key的可讀性會降低;
- 在一個專案中,key最好使用統一的命名模式,例如user:10000:passwd
Strings (字串)
strings 型別是一個很基礎的資料型別,也是任何儲存系統都必備的資料型別,它可以儲存二進位制資料(最大儲存 512 M)
127.0.0.1:6379> set mystr "hello,world!"
OK
127.0.0.1:6379> get mystr
"hello,world!"
127.0.0.1:6379> append mystr hahah #尾部追加
(integer) 17
127.0.0.1:6379> mset k1 11 k2 22 k3 33 #同時設定多個鍵的值
OK
127.0.0.1:6379> mget k1 k2 k3 #同時獲取多個鍵的值
127.0.0.1:6379> set mynum "2"
OK
127.0.0.1:6379> get mynum
"2"
127.0.0.1:6379> incr mynum #遞增
(integer) 3
127.0.0.1:6379> get mynum
"3"
127.0.0.1:6379> decr mynum #遞減
(integer) 3
127.0.0.1:6379> get mynum
"2"
127.0.0.1:6379> incrby num 3 #遞增(增量值)
(integer) 4
127.0.0.1:6379> decrby num 3 #遞減(增量值)
(integer) 1
127.0.0.1:6379> incrbyfloat num 3.3 #遞增/減(浮點數)
"7.3"
-
在遇到數值操作時,redis 會將字串型別轉換成數值
-
由於 INCR 等指令本身就具有原子操作的特性,所以我們完全可以利用 redis 的INCR、INCRBY、DECR、DECRBY 等指令來實現原子計數的效果
Hash(雜湊)
hashes 存的是字串和字串值之間的對映
127.0.0.1:6379> hmset user name antirez password P1pp0 age 34 #建立雜湊,並賦值
OK
127.0.0.1:6379> hgetall user #列出雜湊的內容
1) "username"
2) "antirez"
3) "password"
4) "P1pp0"
5) "age"
6) "34"
127.0.0.1:6379> hset user password 12345 #更改雜湊中的某一個值
(integer) 0
127.0.0.1:6379> hkeys user #獲取所有field值
1) "id"
2) "name"
3) "age"
4) "gender"
127.0.0.1:6379> hvals user #獲取所有value值
1) "2"
2) "wu"
3) "23"
4) "male"
127.0.0.1:6379> hget user name #獲取某個field的value值
"wu"
127.0.0.1:6379> hmget user age gender ##獲取多個field的value值
1) "23"
2) "male"
127.0.0.1:6379> hdel user name #刪除某個field
(integer) 1
127.0.0.1:6379> hlen user #查詢hash長度
(integer) 3
127.0.0.1:6379> hincrby user id 1 #field的value值自增
(integer) 12
List (字串列表)
redis 中的 lists 在底層實現上並不是陣列而是連結串列,也就是說對於一個具有上百萬個元素的 lists 來說,在頭部和尾部插入一個新元素,其時間複雜度是常數級別的。雖然 lists 有這樣的優勢,但同樣有其弊端,那就是,連結串列型 lists 的元素查詢會比較慢,而陣列查詢就會快得多。
127.0.0.1:6379> lpush mylist "1" #新建一個list並列表頭部插入元素
(integer) 1 #返回當前mylist中的元素個數
127.0.0.1:6379> rpush mylist "2" #在mylist頭部插入元素
(integer) 2
127.0.0.1:6379> lpush mylist "0" #在mylist尾部插入元素
(integer) 3
127.0.0.1:6379> lrange mylist 0 1 #列出mylist中從編號0到編號1的元素
1) "0"
2) "1"
127.0.0.1:6379> lrange mylist 0 -1 #列出mylist中從編號0到倒數第一個元素
1) "0"
2) "1"
3) "2"
127.0.0.1:6379> lindex mylist 0 #獲取指定索引的值
"1"
127.0.0.1:6379> lset mylist 0 11 #設定指定位置索引的值
OK
127.0.0.1:6379> lpop mylist #頭部彈出值
"1"
127.0.0.1:6379> rpop mylist #尾部彈出值
"2"
127.0.0.1:6379> llen mylist #獲取list中元素個數
(integer) 1
127.0.0.1:6379> lrem mylist 2 2 #刪除list中的元素(引數1:0表示刪除所有值 引數2:list中指定的值)
(integer) 0
127.0.0.1:6379> linsert mylist before 1 111 #在指定位置before/end插入元素
(integer) 3
Set(字串集合)
Redis 的集合是一種無序的集合,集合中的元素沒有先後順序。
127.0.0.1:6379> sadd myset "one" #向集合myset中加入一個新元素"one"
(integer) 1
127.0.0.1:6379> smembers myset #列出集合myset中的所有元素
1) "one"
127.0.0.1:6379> sismember myset "one" #判斷元素1是否在集合myset中,返回1表示存在
(integer) 1
127.0.0.1:6379> sadd yourset "1" "2"#新建一個新的集合yourset(可以新增多個元素)
(integer) 1
127.0.0.1:6379> sunion myset yourset #對兩個集合求並集
1) "1"
2) "one"
127.0.0.1:6379> srem myset v1 #刪除集合元素
(integer) 1
127.0.0.1:6379> scard myset #獲取集合元素個數
(integer) 7
127.0.0.1:6379> srandmember myset 2 #隨機獲取指定個數的集合元素(負數有重複)
1) "5"
2) "1"
127.0.0.1:6379> spop myset 2 #從頭部開始彈出指定集合個數
1) "3"
2) "4"
ZSet(有序字串集合)
redis 不但提供了無序集合(sets),還很體貼的提供了有序集合(sorted sets)。有序集合中的每個元素都關聯一個序號(score),這便是排序的依據。
127.0.0.1:6379> zadd myzset 1 baidu.com #新增一個有序集合並加入一個元素 baidu.com,給它賦予的序號是1
(integer) 1
127.0.0.1:6379> zadd myzset 3 360.com #向myzset中新增一個元素360.com,賦予它的序號是3
(integer) 1
127.0.0.1:6379> zadd myzset 2 google.com #向myzset中新增一個元素google.com,賦予它的序號是2
(integer) 1
127.0.0.1:6379> zrange myzset 0 -1 with scores #列出myzset的所有元素,同時列出其序號(有序)
1) "baidu.com"
2) "1"
3) "google.com"
4) "2"
5) "360.com"
6) "3"
127.0.0.1:6379> zrange myzset 0 -1 #只列出myzset的元素
1) "baidu.com"
2) "google.com"
3) "360.com"
ZSet是有序集合,從上面的圖中可以看到ZSet的底層實現是ziplist
和skiplist
實現的,ziplist上面已經詳細講過,這裡來講解skiplist的結構實現。
skiplist(跳躍表)
跳躍表是一種有序的資料結構,它通過每一個節點維持多個指向其它節點的指標,從而達到快速訪問的目的。
skiplist有如下幾個特點:
- 有很多層組成,由上到下節點數逐漸密集,最上層的節點最稀疏,跨度也最大。
- 每一層都是一個有序連結串列,至少包含兩個節點,頭節點和尾節點。
- 每一層的每一個每一個節點都含有指向同一層下一個節點和下一層同一個位置節點的指標。
- 如果一個節點在某一層出現,那麼該以下的所有連結串列同一個位置都會出現該節點。
具體實現的結構圖如下所示:
在跳躍表的結構中有 head 和 tail 表示指向頭節點和尾節點的指標,能快速的實現定位。level 表示層數,len 表示跳躍表的長度,BW 表示後退指標,在從尾向前遍歷的時候使用。
BW下面還有兩個值分別表示分值(score)和成員物件(各個節點儲存的成員物件)。
跳躍表的實現中,除了最底層的一層儲存的是原始連結串列的完整資料,上層的節點數會越來越少,並且跨度會越來越大。
跳躍表的上面層就相當於索引層,都是為了找到最後的資料而服務的,資料量越大,條表所體現的查詢的效率就越高,和平衡樹的查詢效率相差無幾。
跳躍表的本質就是建立多級索引,以提高查詢效率