redis 系列一(redis介紹-安裝-數據結構)

Redis 的由來

Redis(全稱:Remote Dictionary Server 遠程字典服務)是一個開源的使用ANSI C語言編寫、支持網絡、可基於內存亦可持久化的日誌型、Key-Value數據庫,並提供多種語言的API。從2010年3月15日起,Redis的開發工作由VMware主持。從2013年5月開始,Redis的開發由Pivotal贊助。

Redis 非關係型數據庫,是互聯網技術領域最爲廣泛使用的存儲中間件 是 Remote Dictionary Service 的縮寫, 超高的性能,完美的文檔,和簡潔的源碼和豐富的(多語言)客戶端庫支持,在開源中間件領域受好評,還有目前發行版本是單線程 所以是安全的所以減少了,許多需要考慮的地方。

使用的公司:

本人在國內Top3的支付公司 也是使用的 Redis,但是redis 也有許多的漏洞,需要運維同學注意。

 

一、爲什麼使用 Redis

  1. 解決應用服務器的cpu和內存壓力

  2. 減少io的讀操作,減輕io的壓力

  3. 關係型數據庫的擴展性不強,難以改變表結構

二、優點:

  1. nosql數據庫沒有關聯關係,數據結構簡單,拓展表比較容易

  2. nosql讀取速度快,對較大數據處理快

三、適用場景:

  1. 數據高併發的讀寫

  2. 海量數據的讀寫

  3. 對擴展性要求高的數據

  4. 防止刷接口

  5. 秒殺

  6. 字典值

  7. 控制開關

  8. 臨時數據

  9. 計算附近的距離

  10. 記錄點贊數 (可以增加和減少)

  11. 快速顯示用戶的列表

  12. 緩存用戶的歷史數據

  13. ...... 還有許多,根據業務場景可以不同的擴展

四、不適場景:

  1. 需要事務支持(非關係型數據庫)

  2. 基於sql結構化查詢儲存,關係複雜

  3. 單線程的能力有限,不能同時處理大量的數據的任務 (好像是redis 6版本中加入了多線程)

 

安裝redis   

環境centos7

百度雲

$ wget http://download.redis.io/releases/redis-5.0.7.tar.gz
$ tar xzf redis-5.0.7.tar.gz
$ cd redis-5.0.7
$ make

$ wget http://download.redis.io/releases/redis-5.0.7.tar.gz

內容有這些文件

  1. 第一步下載
  2. 解壓
  3. 進入目標文件進行make  這裏注意一下 我的這個服務器是新的什麼內容都沒有這有一個報錯
  4.      4步驟中沒有出現可以執行6,有出現4的情況執行5     下載安裝 gcc   :直接通過命令: 原因redis 是c語言寫的 編譯需要使用到gcc
  5. yum install gcc-c++ -y
  6.      進入文件夾 執行命令  make 有可能會有報錯
  7. 如果有7這樣的錯誤執行 8,同理沒有執行9  make MALLOC=libc
  8. 修改 redis.conf 文件 改爲yes

    daemonize yes

  9. 注意版本不同進行啓動的地方也不一樣 我的是最新的 5.0.7 版本 redis-server 是在 src / 下的
  10.  啓動命令 

    ./redis-server ./redis.conf &

  11. 啓動好的土星是這樣的

 

Redis 的安裝還是非常的簡單的。

後期有時間寫一篇博客來具體說一下 redis 的集羣是如何搭建的

 

Redis 數據結構:

redis 有5種數據結構  分別是 String(字符串),List(集合),set(集合),zset(有序集合),hash(字典)是redis 的基礎,也是最重要的地方。

Redis 的所有的數據結構都是這樣的一個唯一的key作爲名稱,通過這個key來獲取對應的數據(value) 不同的是value的數據結構是不一樣的。

String(字符串) :String 是redis 中最簡單的,字符串結構非常廣泛,一般保存用戶的信息,商品的信息,配置信息,key是設定的+id號, value是通過數據庫的信息在json 轉換一下保存到redis中來緩存,(注意DO對象需要序列化,通過實現Serializable)在通過redis 反序列化讀取出來。Redis 的字符串是動態字符串,是可以進行修改的字符串,內部的實現類似java的ArrayList,採取的是預分配空間和動態擴容,目的是爲了減少內存的頻繁的分配。擴容的原理是小於1MBdouble空間,大於1MB加1MB,注意最大空間爲512M。

舉例通過命令的增加  key就是name 對應的value是xuxiaoguan  這是簡單的 一個key value的加入

127.0.0.1:6379> set name xuxiaoguan
OK
127.0.0.1:6379> get name
"xuxiaoguan"
127.0.0.1:6379> EXISTS name
(integer) 1
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)

現在是批量的加入數據 模板格式 mset  mget

127.0.0.1:6379> mset name1 boy1 name2 boy2
OK
127.0.0.1:6379> mget name1 name2
1) "boy1"
2) "boy2"
127.0.0.1:6379> 

對鍵值對進行設置過期時間 這樣利用空間,過期自動進行刪除,一般用來控制緩存的過期的時間

127.0.0.1:6379> set name xuxiaoguan 
OK
127.0.0.1:6379> get name
"xuxiaoguan"
// 這裏開始設置過期的時間爲3秒需要指定的key
127.0.0.1:6379> expire name  3
(integer) 1
127.0.0.1:6379> get name
(nil)

計數

value 是一個整數,可以進行自增的,但是有範圍的,(signed long的最大值和最小值之間)超過了會報錯的

127.0.0.1:6379> set SetAgeValue 60
OK
127.0.0.1:6379> incr SetAgeValue
(integer) 61
127.0.0.1:6379> incrby  SetAgeValue 9
(integer) 70
127.0.0.1:6379> incrby  SetAgeValue -20
(integer) 50

 

List (列表)

Redis 的list和Java的Arraylist 差不多,這裏的list 是鏈表(java是數組)插入和刪除的速度非常快時間複雜度是o(1) ,但是查找和指定插入就會浪費時間o(n),這會讓許多人感到意外。 list使用的是雙向指針。 列表中沒有了元素了,會進行自動的刪除,節省空間。

redis 的隊列結構通常用來做異步對列,先進先出,將需要處理的結構體序列化後放入對列中,另外一個線程進行讀取(消費)

127.0.0.1:6379> rpush book python java go
(integer) 3
127.0.0.1:6379> llen book
(integer) 3
127.0.0.1:6379> lpop book
"python"
127.0.0.1:6379> lpop book
"java"
127.0.0.1:6379> lpop book
"go"
127.0.0.1:6379> lpop book
(nil)

數據結構中有一種類型是 棧:格式是先進後出的,和隊列是向反的

127.0.0.1:6379> rpush book python java go
(integer) 3
127.0.0.1:6379> rpop book
"go"
127.0.0.1:6379> rpop book
"java"
127.0.0.1:6379> rpop book
"python"
127.0.0.1:6379> rpop book
(nil)

Redis  list 其實還不像linkedList 往下看 更是一個zipList ,將所有的數據雙向指針精密連接儲存的,分配的是一塊連續的內存。當數據量比較大的時候改爲quickList, 所以說Redis的List是zipList+quickList組成的。  quickList 滿足了快速的插入和刪除的,

hash(字典)

Redis 的字典和java(1.7版本以前)的hashmap區別不大的,都是數組+鏈表,hash碰撞的使用鏈表串起來的。

Redis和java中的區別點:

  1. java 中的key是可以任意類型支持自定義的,但是redis hash的key只可以是String類型的。
  2. java 中的rehash 當hash很小或很大時,都是一次性的rehash 會很耗時,但是Redis因爲是單線程的,爲了高性成的,爲了低耗時,不阻塞,所以採用了,漸進式rehash。

漸進式rehash:同時分配出新的空間出來,保持原有的,新加入的數據指向新的hash中,(查詢的時候也會指向這兩個hash),後續通過定時任務來合併這兩個hash,一點點的遷移,這裏不是用的單線程,使用的是子線程。數據遷移完成後,會自動的刪除原來的hash,指向新的hash。

hash 可以保存數據類型挺多的,比如用戶的信息,不同的字段對應不同的值。獲取某一個值可以直接獲取,不需要像String一樣全部的獲取到,浪費網絡的流量。

但是hash也是有缺點的:hash 的結構存儲高於單個字符串,所以我們在開發中使用hash還是String 我們自己需要衡量一下。

127.0.0.1:6379> hset javaSet java  "think in java"
(integer) 1
127.0.0.1:6379> hset javaSet python CodePython
(integer) 1
127.0.0.1:6379> hset javaSet go CodeGo
(integer) 1
127.0.0.1:6379> hgetall javaSet             ## 注意這裏key 和 value是隔行展示的
1) "java"
2) "think in java"
3) "python"
4) "CodePython"
5) "go"
6) "CodeGo"
127.0.0.1:6379> hlen javaSet
(integer) 3
127.0.0.1:6379> hget javaSet go
"CodeGo"
127.0.0.1:6379> hset javaSet go "happy code"
(integer) 0
127.0.0.1:6379> hget javaSet go
"happy code"

hash 的操作命令有這些

 

Set(集合)

Redis 的set和 java中的hashSet 區別不大的,內部的鍵值對都是無序的。不同的是Redis 的set對應的value值都是Null。

當集合中最後一個元素被移除完,數據結構被自動的刪除,內存收回。

127.0.0.1:6379> sadd books java
(integer) 1
127.0.0.1:6379> sadd books go
(integer) 1
127.0.0.1:6379> sadd books python
(integer) 1
127.0.0.1:6379> smembers books
1) "go"
2) "python"
3) "java"
## 從這裏就可以看的出來set
127.0.0.1:6379> scard books  ## 數據的長度
(integer) 3
127.0.0.1:6379> spop books   ## 彈出一個數據
"go"
127.0.0.1:6379> sismember books java   ## 數據中是否包含
(integer) 1
127.0.0.1:6379> sismember books python
(integer) 1
127.0.0.1:6379> sismember books gos
(integer) 0

Zset(有序列表)

類似java中的SortedSet 和 HashMap 的結合體。

  1.  保證了內部value 的唯一性,同時還給每個value 加入了Score,代表這個value 的權重(我個人認爲和優先級的定義差不多的)
  2. zset最後一個數據被刪除,數據結構被刪除,內存會自動的收回。
  3. zset可以用來保存用戶的收藏更具時間的順序加入,對應文章#活動#商品的id號。
  4. 可以保存學生的數據比如學生的分數可以直接進行排序。
127.0.0.1:6379> zadd javaBook  9.0  "think code in java One"
(integer) 1
127.0.0.1:6379> zadd javaBook  8.9  "think code in java Two"
(integer) 1
127.0.0.1:6379> zadd javaBook  8.6  "think code in java Three"
(integer) 1
127.0.0.1:6379> zrange javaBook 0 -1   ## 正序的展示的  從小到大
1) "think code in java Three"
2) "think code in java Two"
3) "think code in java One"
127.0.0.1:6379> zrevrange javaBook 0 -1   ## 倒敘的展示 從大到小
1) "think code in java One"
2) "think code in java Two"
3) "think code in java Three"

Zset的內部使用的是 “跳躍列表”來實現的,比較特殊所以也就比較複雜。 可以和世界地圖差不多的,世界->中國->江蘇->南通->某市->某街道->門牌號  是一個道理的。 應爲這些元素可能身間數職。

定位插入數據的時候,先從頂層定位,然後潛伏到下一級,一直到最合適的層級然後將數據插入進去。

而Redis的索引被提取爲多層。如圖:

所有的元素都會在L0層的鏈表中,根據分數進行排序,同時會有一部分節點有機會被抽取到L1層中,作爲一個稀疏索引,同樣L1層中的索引也有一定機會被抽取到L2層中,組成一個更稀疏的索引列表。

下面用圖來演示一下在對快速鏈表進行插入、刪除、查詢時,是如何定位到L0層中的具體位置的。

首先,假定有這麼一個鏈表,注意這裏只展示分數,而不展示具體的值了:

如果要查找分數爲66的元素,首先在L2層的索引找。很明顯,66位於25和85中間,這時就縮小了查找區間:

然後根據獲得的區間,去L1對應的區間中查找,得到一個更精確的區間:

最終,根據這個更精確的區間,去L0層順序遍歷,即可得到要查找的元素:

上述即是對Redis的跳躍表的原理的一個簡述。

這種跳躍表的實現,其實和二分查找的思路有點接近,只是一方面因爲二分查找只能適用於數組,而無法適用於鏈表,所以爲了讓鏈表有二分查找類似的效率,就以空間換時間來達到目的。

跳躍表因爲是一個根據分數權重進行排序的列表,可以再很多場景中進行應用,比如排行榜,搜索排序等等。

容器型數據結構:  list  hash set zset   都是容器型數據結構,都遵守2大規定。

  1. create if not exists 如果容器不在,創建一個,在進行操作。
  2. 數據結構中沒有數據,會自動刪除數據,釋放內存

過期時間

Redis 的所有數據結構都可以進行設置過期時間的,

注意點

  1. hash 需要注意到了過期時間自動刪除的,不是某一個子key 的刪除。
  2. 如果進行設置了值同時設置了過期時間,後面進行修改了原來的過期時間是作廢了。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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