Redis數據庫介紹

引言

http://doc.redisfans.com/

http://www.redis.cn/commands.html

redis是一個開源的、使用C語言編寫的、支持網絡交互的、可基於內存也可持久化的Key-Value數據庫。

redis數據結構

redis是一種高級的key:value存儲系統,其中value支持五種數據類型:

字符串(strings)

字符串列表(lists)

字符串集合(sets)

有序字符串集合(sorted sets)

哈希(hashes)

而關於key,有幾個點要提醒大家:

key不要太長,儘量不要超過1024字節,這不僅消耗內存,而且會降低查找的效率;

key也不要太短,太短的話,key的可讀性會降低;

在一個項目中,key最好使用統一的命名模式,例如user:10000:passwd。

redis數據結構 – strings

有人說,如果只使用redis中的字符串類型,且不使用redis的持久化功能,那麼,redis就和memcache非常非常的像了。這說明strings類型是一個很基礎的數據類型,也是任何存儲系統都必備的數據類型。

我們來看一個最簡單的例子:

set mystr "hello world!" //設置字符串類型

get mystr //讀取字符串類型

字符串類型的用法就是這麼簡單,因爲是二進制安全的,所以你完全可以把一個圖片文件的內容作爲字符串來存儲。

另外,我們還可以通過字符串類型進行數值操作:

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"

看,在遇到數值操作時,redis會將字符串類型轉換成數值。

由於INCR等指令本身就具有原子操作的特性,所以我們完全可以利用redis的INCR、INCRBY、DECR、DECRBY等指令來實現原子計數的效果,假如,在某種場景下有3個客戶端同時讀取了mynum的值(值爲2),然後對其同時進行了加1的操作,那麼,最後mynum的值一定是5。不少網站都利用redis的這個特性來實現業務上的統計計數需求。

redis數據結構 – lists

redis的另一個重要的數據結構叫做lists,翻譯成中文叫做“列表”。

首先要明確一點,redis中的lists在底層實現上並不是數組,而是鏈表,也就是說對於一個具有上百萬個元素的lists來說,在頭部和尾部插入一個新元素,其時間複雜度是常數級別的,比如用LPUSH在10個元素的lists頭部插入新元素,和在上千萬元素的lists頭部插入新元素的速度應該是相同的。

雖然lists有這樣的優勢,但同樣有其弊端,那就是,鏈表型lists的元素定位會比較慢,而數組型lists的元素定位就會快得多。

lists的常用操作包括LPUSH、RPUSH、LRANGE等。我們可以用LPUSH在lists的左側插入一個新元素,用RPUSH在lists的右側插入一個新元素,用LRANGE命令從lists中指定一個範圍來提取元素。我們來看幾個例子:

//新建一個list叫做mylist,並在列表頭部插入元素"1"

127.0.0.1:6379> lpush mylist "1"

//返回當前mylist中的元素個數

(integer) 1

//在mylist右側插入元素"2"

127.0.0.1:6379> rpush mylist "2"

(integer) 2

//在mylist左側插入元素"0"

127.0.0.1:6379> lpush mylist "0"

(integer) 3

//列出mylist中從編號0到編號1的元素

127.0.0.1:6379> lrange mylist 0 1

1) "0"

2) "1"

//列出mylist中從編號0到倒數第一個元素

127.0.0.1:6379> lrange mylist 0 -1

1) "0"

2) "1"

3) "2"

lists的應用相當廣泛,隨便舉幾個例子:

我們可以利用lists來實現一個消息隊列,而且可以確保先後順序,不必像MySQL那樣還需要通過ORDER BY來進行排序。

利用LRANGE還可以很方便的實現分頁的功能。

redis數據結構 – 集合

redis的集合,是一種無序的集合,集合中的元素沒有先後順序。

集合相關的操作也很豐富,如添加新元素、刪除已有元素、取交集、取並集、取差集等。我們來看例子:

//向集合myset中加入一個新元素"one"

127.0.0.1:6379> sadd myset "one"

(integer) 1

127.0.0.1:6379> sadd myset "two"

(integer) 1

//列出集合myset中的所有元素

127.0.0.1:6379> smembers myset

1) "one"

2) "two"

//判斷元素1是否在集合myset中,返回1表示存在

127.0.0.1:6379> sismember myset "one"

(integer) 1

//判斷元素3是否在集合myset中,返回0表示不存在

127.0.0.1:6379> sismember myset "three"

(integer) 0

//新建一個新的集合yourset

127.0.0.1:6379> sadd yourset "1"

(integer) 1

127.0.0.1:6379> sadd yourset "2"

(integer) 1

127.0.0.1:6379> smembers yourset

1) "1"

2) "2"

//對兩個集合求並集

127.0.0.1:6379> sunion myset yourset

1) "1"

2) "one"

3) "2"

4) "two"

對於集合的使用,也有一些常見的方式,比如,QQ有一個社交功能叫做“好友標籤”,大家可以給你的好友貼標籤,比如“大美女”、“土豪”、“歐巴”等等,這時就可以使用redis的集合來實現,把每一個用戶的標籤都存儲在一個集合之中。

redis數據結構 – 有序集合

redis不但提供了無需集合(sets),還很體貼的提供了有序集合(sorted sets)。有序集合中的每個元素都關聯一個序號(score),這便是排序的依據。

很多時候,我們都將redis中的有序集合叫做zsets,這是因爲在redis中,有序集合相關的操作指令都是以z開頭的,比如zrange、zadd、zrevrange、zrangebyscore等等

老規矩,我們來看幾個生動的例子:

//新增一個有序集合myzset,並加入一個元素baidu.com,給它賦予的序號是1:

127.0.0.1:6379> zadd myzset 1 baidu.com

(integer) 1

//向myzset中新增一個元素360.com,賦予它的序號是3

127.0.0.1:6379> zadd myzset 3 360.com

(integer) 1

//向myzset中新增一個元素google.com,賦予它的序號是2

127.0.0.1:6379> zadd myzset 2 google.com

(integer) 1

//列出myzset的所有元素,同時列出其序號,可以看出myzset已經是有序的了。

127.0.0.1:6379> zrange myzset 0 -1 withscores

1) "baidu.com"

2) "1"

3) "google.com"

4) "2"

5) "360.com"

6) "3"

//只列出myzset的元素

127.0.0.1:6379> zrange myzset 0 -1

1) "baidu.com"

2) "google.com"

3) "360.com"

redis數據結構 – 哈希

最後要給大家介紹的是hashes,即哈希。哈希是從redis-2.0.0版本之後纔有的數據結構。

hashes存的是字符串和字符串值之間的映射,比如一個用戶要存儲其全名、姓氏、年齡等等,就很適合使用哈希。

我們來看一個例子:

//建立哈希,並賦值

127.0.0.1:6379> HMSET user:001 username antirez password P1pp0 age 34

OK

//列出哈希的內容

127.0.0.1:6379> HGETALL user:001

1) "username"

2) "antirez"

3) "password"

4) "P1pp0"

5) "age"

6) "34"

//更改哈希中的某一個值

127.0.0.1:6379> HSET user:001 password 12345

(integer) 0

//再次列出哈希的內容

127.0.0.1:6379> HGETALL user:001

1) "username"

2) "antirez"

3) "password"

4) "12345"

5) "age"

6) "34"

用法

像MySQL一樣,redis是支持主從同步的,而且也支持一主多從以及多級從結構。

主從結構,一是爲了純粹的冗餘備份,二是爲了提升讀性能,比如很消耗性能的SORT就可以由從服務器來承擔。

redis的主從同步是異步進行的,這意味着主從同步不會影響主邏輯,也不會降低redis的處理性能。

主從架構中,可以考慮關閉主服務器的數據持久化功能,只讓從服務器進行持久化,這樣可以提高主服務器的處理性能。

在主從架構中,從服務器通常被設置爲只讀模式,這樣可以避免從服務器的數據被誤修改。但是從服務器仍然可以接受CONFIG等指令,所以還是不應該將從服務器直接暴露到不安全的網絡環境中。如果必須如此,那可以考慮給重要指令進行重命名,來避免命令被外人誤執行。

同步原理

從服務器會向主服務器發出SYNC指令,當主服務器接到此命令後,就會調用BGSAVE指令來創建一個子進程專門進行數據持久化工作,也就是將主服務器的數據寫入RDB文件中。在數據持久化期間,主服務器將執行的寫指令都緩存在內存中。

在BGSAVE指令執行完成後,主服務器會將持久化好的RDB文件發送給從服務器,從服務器接到此文件後會將其存儲到磁盤上,然後再將其讀取到內存中。這個動作完成後,主服務器會將這段時間緩存的寫指令再以redis協議的格式發送給從服務器。

另外,要說的一點是,即使有多個從服務器同時發來SYNC指令,主服務器也只會執行一次BGSAVE,然後把持久化好的RDB文件發給多個下游。在redis2.8版本之前,如果從服務器與主服務器因某些原因斷開連接的話,都會進行一次主從之間的全量的數據同步;而在2.8版本之後,redis支持了效率更高的增量同步策略,這大大降低了連接斷開的恢復成本。

主服務器會在內存中維護一個緩衝區,緩衝區中存儲着將要發給從服務器的內容。從服務器在與主服務器出現網絡瞬斷之後,從服務器會嘗試再次與主服務器連接,一旦連接成功,從服務器就會把“希望同步的主服務器ID”和“希望請求的數據的偏移位置(replication offset)”發送出去。主服務器接收到這樣的同步請求後,首先會驗證主服務器ID是否和自己的ID匹配,其次會檢查“請求的偏移位置”是否存在於自己的緩衝區中,如果兩者都滿足的話,主服務器就會向從服務器發送增量內容。

增量同步功能,需要服務器端支持全新的PSYNC指令。這個指令,只有在redis-2.8之後才具有。

聊聊redis的事務處理

衆所周知,事務是指“一個完整的動作,要麼全部執行,要麼什麼也沒有做”。

在聊redis事務處理之前,要先和大家介紹四個redis指令,即MULTI、EXEC、DISCARD、WATCH。這四個指令構成了redis事務處理的基礎。

MULTI用來組裝一個事務;

EXEC用來執行一個事務;

DISCARD用來取消一個事務;

WATCH用來監視一些key,一旦這些key在事務執行之前被改變,則取消事務的執行。

實戰

我們先拋出問題,在廣告程序化交易的過程中,我們經常需要爲一個廣告投放計劃定製人羣包,其存儲的形式如下:

人羣包ID => [設備ID_1, 設備ID_2 ... 設備ID_N]

其中,人羣包ID是Long型整數,設備ID是經過MD5處理,長度爲32。

在業務場景中,我們需要判斷一個設備ID是否在一個人羣包中,來決定是否投放廣告。

在傳統的使用Redis的場景, 我們可以使用標準的KV結構來存儲定向包數據,則存儲方式如下:

{人羣包ID}_{設備ID_1} => true

{人羣包ID}_{設備ID_2} => true

如果我們想使用ziplist來繼續內存壓縮的話,我們必須保證Hash對象的長度小於512,並且鍵值的長度小於64字節。 我們可以將KV結構的數據,存儲到預先分配好的bucket中。

我們先預估下,整個Redis集羣預計容納的數據條數爲10億,那麼Bucket的數量的計算公式如下:

bucket_count = 10億 / 512 = 195W

那麼我們大概需要200W個Bucket(預估Bucket數量需要多預估一點,以防觸發臨界值問題)

我們先以下公式計算BucketID:

bucket_id = CRC32(人羣包ID + "_" + 設備ID) % 200W

那麼數據在Redis的存儲結構就變成

bucket_id => {

{人羣包ID}_{設備ID_1} => true

{人羣包ID}_{設備ID_2} => true

}

這樣我們保證每個bucket中的數據項都小於512,並且長度均小於64字節。

我們以2000W數據進行測試,前後兩者的內存使用情況如下:

數據集大小 存儲模式 Bucket數量 所用內存 碎片率 Redis佔用的內存
2000W 壓縮列表 200W 928M 1.38 1.25G
2000W 壓縮列表 5W 785M 1.48 1.14G
2000W 直接存儲 - 1.44G 1.03 1.48G

在這裏需要額外引入一個概念 -- 內存碎片率。

內存碎片率 = 操作系統給Redis分配的內存 / Redis存儲對象佔用的內存

因爲壓縮列表在更新節點的時候,經常需要進行內存重分配,所以導致比較高的內存碎片率。我們在做技術方案比較的時候,內存碎片率也是非常需要關注的指標之一。

但有很多手段可以減少內存碎片率,比如內存對其,甚至更極端的直接重做整個Redis內存(利用快照或者從節點來重做內存)都能有效的減低內存碎片率。

我們在本次實驗中,因爲存儲的數值比較大(單個KEY約34個字節),所以實際節省內存不是很多,但依然能節約35%-50%的內存使用。

在實際的生產環境中,我們根據應用場景合理的設計壓縮存儲結構,部分業務甚至能達到節約70%的內存使用的效果。

總結

以 上就是我對Java開發大型互聯網Redis數據庫介紹之redis數據庫實戰及其優化總結,分享給大家,希望大家知道什麼是Java開發大型互聯網Redis數據庫介紹之redis數據庫實戰問題及其優化。覺得收穫的話可以點個關注收藏轉發一波喔,謝謝大佬們支持!

1、多寫多敲代碼,好的代碼與紮實的基礎知識一定是實踐出來的

2、可以去百度搜索騰訊課堂圖靈學院的視頻來學習一下java架構實戰案例,還挺不錯的。

最後,每一位讀到這裏的網友,感謝你們能耐心地看完。希望在成爲一名更優秀的Java程序員的道路上,我們可以一起學習、一起進步!

3丶想了解學習以上課程內容可加羣:32848930

Redis 密碼設置和查看密碼

redis沒有實現訪問控制這個功能,但是它提供了一個輕量級的認證方式,可以編輯redis.conf配置來啓用認證。

   1、初始化Redis密碼:

   在配置文件中有個參數: requirepass  這個就是配置redis訪問密碼的參數;

   比如 requirepass test123;

   (Ps:需重啓Redis才能生效)

   redis的查詢速度是非常快的,外部用戶一秒內可以嘗試多大150K個密碼;所以密碼要儘量長(對於DBA 沒有必要必須記住密碼);

   2、不重啓Redis設置密碼:

   在配置文件中配置requirepass的密碼(當redis重啓時密碼依然有效)。

   redis 127.0.0.1:6379> config set requirepass test123

   查詢密碼:

   redis 127.0.0.1:6379> config get requirepass
   (error) ERR operation not permitted

   密碼驗證:

   redis 127.0.0.1:6379> auth test123
   OK

   再次查詢:

   redis 127.0.0.1:6379> config get requirepass
   1) "requirepass"
   2) "test123"

   PS:如果配置文件中沒添加密碼 那麼redis重啓後,密碼失效;

   3、登陸有密碼的Redis:

   在登錄的時候的時候輸入密碼:

   redis-cli -p 6379 -a test123

   先登陸後驗證:

   redis-cli -p 6379

   redis 127.0.0.1:6379> auth test123
   OK

   AUTH命令跟其他redis命令一樣,是沒有加密的;阻止不了攻擊者在網絡上竊取你的密碼;

   認證層的目標是提供多一層的保護。如果防火牆或者用來保護redis的系統防禦外部攻擊失敗的話,外部用戶如果沒有通過密碼認證還是無法訪問redis的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章