1.屬於什麼類型的數據庫html
not only sql 非關係型數據庫,與傳統的關係型數據庫不一樣,存儲形式都是kv形式。mysql
2.特色redis
幾乎不支持事務,key-value形式存儲,支持隊列和緩存(能夠設置數據的過時時間)sql
2.1 數據存儲的持久化數據庫
能夠將內存中的數據保存在磁盤上,重啓是能夠加載磁盤的內容進行使用緩存
2.2 多樣的數據存儲類型安全
list,set,zset,hash 等數據結構redis都支持服務器
2.3 支持數據備份網絡
master-slave模式的數據備份,哨兵機制。數據結構
3.redis的通常基本配置
redis.conf----配置文件
須要修改讀寫權限進行操做
sudo chmod 777 ××× (每一個人都有讀和寫以及執行的權限)
cd 到 redis.conf 的文件路徑,修改操做權限
sudo chmod 777 redis.conf
輸入ubantu的密碼
gidit redis.conf打開redis.conf
默認IP是127.0.0.1 端口號是 6379
須要遠程控制,或者公用redis的話,須要修改IP爲真實的IP
默認的數據文件存儲路徑爲 dir /var/lib/redis
4.redis的使用
4.1啓動服務器
redis-server
4.2啓動客戶端
redis-cli
啓動客戶端,默認是鏈接0號數據庫,默認有16個數據庫(0-15)。
select 8 切換到8號數據庫
redsi的數據存儲結構
key是不能重複的字符串,值支持五種數據類型:字符串、列表、集合、散列表、有序集合
5.數據庫的增刪改查
5.1 string 字符串類型
set name kobe set + key + value
setex age 3 18 setex key time value 設置過時時間爲time
mset name1 a name2 b name3 c mset設置多個鍵值對
append name1 opy 給指定鍵追加值,值會按照字符串進行拼接
mget name1 name2 name3 獲取多個值
keys pattern 鍵支持正則表達
keys * 查看全部的鍵
keys a* 以a開頭的鍵
keys *a* 包含a的鍵
keys *a 以a結尾的鍵
exists key 查看鍵是否存在 1 表示存在 0 表示不存在
type key 查看鍵對應的值得類型
del key 刪除鍵,值會自動刪除
5.2 hasn 類型
hash用於存儲對象,對象是由鍵與值構成,值的類型爲string, Redis hash 是一個string類型的field和value的映射表
hset user name nash 設置user對象的name的值爲nash
hkeys user 獲取user對象的全部鍵
hget user "name" 獲取user對象name的值
hdel user "name" 刪除user對象name鍵
hash的理解:hash存儲的類型是對象,結構相似於 key = {"name":"zhangsan","age":"18"} , 至關於存了一張表中的部分字段,知道key就能夠查看全部的字段,而後取出每一個字段的值,若是分開存爲string類型的話,很差取。
使用場景:好比咱們要存儲一個用戶信息對象數據,包含如下信息:用戶ID爲查找的key,存儲的value用戶對象包含姓名,年齡,生日等信息,若是用普通的key/value結構來存儲。
方式一:ID爲查找的key,對象的其餘信息封裝成一個序列化的方式進行存儲,缺點:增長了序列化和反序列化的開銷,而且,在修改數據時,須要進行併發保護,同時修改某個信息時,同一時間必須一我的操做,操做完畢後,另我的才能操做,還要引入鎖等安全考量。
方式二:ID爲查找的key,能夠這樣存儲,1 = {"name":"zhang"} 1 = {"age":"18"} 1 = {"gender":"1"} ,這樣優勢:省去了序列化和反序列化的開銷,也解決了安全問題,可是若是一個用戶的信息字段特別多,會形成大量的內存開銷。
方式三:ID爲查找的key,key對應的值,其實就是一個Map,Map的field是成員的屬性,value是屬性的值。操做成員的屬性值,直接經過 key(用戶ID) + field(屬性標籤)就能夠操做對應屬性數據了,不用重複存儲數據,也不會帶來序列化和併發修改控制的問題。缺點:redis是單線程模式,hgetall key 能夠查到 Map中的全部filed-value,至關於遍歷了整個映射,若是filed-value的數量太多,會形成耗時等待。
# TODO redis是怎麼解決同時修改數據的安全性問題呢?
# TODO 網上說的redis和mysql數據的一致性怎麼解決?
5.3 list 類型
列表的元素類型爲string
lpush key value1 value2 從左側插入
lpush name kobe nash ball [ball,nash,kobe]
lrange name 0 -1 獲取name列表中的全部元素值
使用場景:消息隊列, # TODO 分佈式爬蟲時候用到 後續增長
5.4 set類型
set 的特色:去重,元素類型爲string型,不支持修改
sadd a zhang wang li zhao :設置 集合 a 元素爲 zhang wang li zhao
smembers a :查看集合a裏面的元素
srem a zhang 刪除集合中的指定元素
# TODO 分佈式爬蟲的時候用到set去重。
zset 有序集合
有序:依靠權重,即集合中的每個元素都會關聯一個double類型的score值,根據值的大小進行排序。從小到大排序
zadd s 4 zhang 5 wang 3 zhao 2 li 1 sun 有序集合的設置
zrange s 0 -1 有序集合元素查看
zrangebyscore s 4 5 返回score值在min和max之間的元素 zhang wang
zscore s zhang 返回元素zhang的score(權重)值 4
zcard s 返回s中有多少個元素
6. redis的優缺點
6.1 速度快
1.純內存操做
絕大部分請求是純粹的內存操做,很是快速。數據存在內存中,相似於HashMap,HashMap的優點就是查找和操做的時間複雜度都是O(1);若是數據一直存在內存中的話,那麼關機後數據是否是就不見了?固然不是,redis中提供了兩種方案,實現數據的持久化存儲,哪兩種方案呢?
1.1 快照方式
是Redis默認的數據存儲方式,指定時間間隔裏面,將數據寫入指定的磁盤文件中,即dump.rdb文件,Reids重啓後會加載這個文件裏面的數據到內存中。
快照方式的配置,在reids.conf文件的SNAPSHOTTING 中,具體參數和配置詳見http://www.javashuo.com/article/p-kbfkhfzd-mh.html。
1.2 觸發快照模式
1. 指定的時間間隔內,執行指定次數的寫操做。
2. 執行save(阻塞),或者bgsave(異步)命令。
3. 執行 flushall 命令,清空數據庫的全部數據。
4. 執行 shutdown命令,關係服務器。
1.3 恢復數據
dumps.rdb拷貝到redis的安裝目錄的bin目錄下,重啓redis便可。通常開發中,會考慮到物理機的磁盤損壞,會選擇備份dump.rdb文件。
1.4 快照的優缺點
優勢:
1.適合大規模的數據恢復。
2.針對數據的完整新和一致性要求不高的業務。
缺點:
1.數據的存儲可能完整性不高,由於存在在最後一次備份的過程當中,宕機了。
2.備份數據是佔用內存,redis在備份時,會建立一個獨立的子進程,將數據寫入臨時文件(此時內存中就會有兩份同樣的數據,一份是內存中存在的數據,一份是臨時數據),最後在用臨時文件替換以前的備份文件。所以redis的數據恢復須要在系統不忙的深夜進行比較合理。
1.5 追加方式
Redis 默認不開啓。它的出現是爲了彌補RDB的不足(數據的不一致性),因此它採用日誌的形式來記錄每一個寫操做,並追加到文件中。Redis 重啓的會根據日誌文件的內容將寫指令從前到後執行一次以完成數據的恢復工做。注意:追加的不是數據,是每一次操做的指令。
1.6 追加方式的觸發
根據文件設置的狀況觸發,能夠每執行一次命令觸發一次,也能夠是每秒觸發一次,具體見:http://www.javashuo.com/article/p-kbfkhfzd-mh.html。
1.7 追加的優缺點
優勢:數據的完整性和一致性更高
缺點:由於AOF記錄的內容多,文件會愈來愈大,數據恢復也會愈來愈慢。
1.8 關於redis持久化的總結
1. 默認開啓快照模式。
2. 若是用redis作緩存的話,能夠不用開啓快照模式。
3. 快照模式適合大面積的恢復數據,可是數據的一致性不高。
4.追加模式須要手動啓動。
5. 追加模式恢復數據的一致性高,可是恢復效率較低。
6. 若是考慮用redis作持久化存儲,建議快照和追加模式都開啓。
2.單線程模式
能避免上下文切換也不存在多進程或者多線程致使的切換而消耗 CPU,不用去考慮各類鎖的問題,不存在加鎖釋放鎖操做,沒有由於可能出現死鎖而致使的性能消耗
3.非阻塞的IO多路複用
多路I/O複用模型是利用 select、poll、epoll 能夠同時監察多個流的 I/O 事件的能力,在空閒的時候,會把當前線程阻塞掉,當有一個或多個流有 I/O 事件時,就從阻塞態中喚醒,因而程序就會輪詢一遍全部的流(epoll 是隻輪詢那些真正發出了事件的流),而且只依次順序的處理就緒的流,這種作法就避免了大量的無用操做。
這裏「多路」指的是多個網絡鏈接,「複用」指的是複用同一個線程。採用多路 I/O 複用技術可讓單個線程高效的處理多個鏈接請求(儘可能減小網絡 IO 的時間消耗),且 Redis 在內存中操做數據的速度很是快,也就是說內存內的操做不會成爲影響Redis性能的瓶頸,主要由以上幾點造就了 Redis 具備很高的吞吐量。
7 redis的過時策略以及內存淘汰機制
1. 過時策略
redis採用的是按期刪除+惰性刪除策略。按期刪除,redis默認每一個100ms檢查,是否有過時的key,有過時key則刪除。須要說明的是,redis不是每一個100ms將全部的key檢查一次,而是隨機抽取進行檢查(若是每隔100ms,所有key進行檢查,redis豈不是卡死)。所以,若是隻採用按期刪除策略,會致使不少key到時間沒有刪除。
因而,惰性刪除派上用場。也就是說在你獲取某個key的時候,redis會檢查一下,這個key若是設置了過時時間那麼是否過時了?若是過時了此時就會刪除。不是的,若是按期刪除沒刪除key。而後你也沒即時去請求key,也就是說惰性刪除也沒生效。這樣,redis的內存會愈來愈高。那麼就應該採用內存淘汰機制。
2. 內存淘汰機制
redis.conf中有配置,allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key。推薦使用,目前項目在用這種。配置見:http://www.javashuo.com/article/p-vifhsouh-kq.html。
若是沒有設置 expire 的key,那麼內存滿的時候,再新寫入數據就會報錯。
8. redis的主從複製
一臺主機master能夠擁有多從機slave,一臺從機slave又能夠擁有多個從機slave。下圖的redis的集羣架構,是經常使用的一種架構,主要有兩個特色:
1. 高擴展性:下面的從機能夠隨時配置,不影響架構。
2. 高可用性:一臺reids從機故障,不影響總體的讀數據,能夠從其餘從機讀取數據。要是主機壞了,會有哨兵機制進行維護。
具體搭建:http://www.javashuo.com/article/p-kbfkhfzd-mh.html。 # TODO 後續本身實現搭建
那麼如何保證主機掛了,架構還能繼續運行呢? 哨兵機制就派上了用場。哨兵主要作三件事:
1. 監視:不斷的監視master和slave是否運行正常。
2. 提醒:當某個redis出現故障的時候,哨兵會經過API向管理員或其餘程序發出通知。
3. 故障遷移:當主機出現故障時,會哨兵會自動將該主機下的某一個從機設置爲新的主機,並讓其餘從機和新主機創建主從關係。
哨兵(sentinel) 是一個分佈式系統,你能夠在一個架構中運行多個哨兵(sentinel) 進程,這些進程使用流言協議(gossipprotocols)來接收關於Master是否下線的信息,並使用投票協議(agreement protocols)來決定是否執行自動故障遷移,以及選擇哪一個Slave做爲新的Master。
每一個哨兵(sentinel) 會向其它哨兵(sentinel)、master、slave定時發送消息,以確認對方是否」活」着,若是發現對方在指定時間(可配置)內未迴應,則暫時認爲對方已掛(所謂的」主觀認爲宕機」 Subjective Down,簡稱sdown).
9. redis的雪崩機制
10 redis作緩存與數據庫的數據一致性問題
# TODO 須要時間消化 http://www.javashuo.com/article/p-ftdcqtnl-cx.html。
11. redis如何解決高併發問題。
redis配置文件中有最大鏈接數量,maxclients能夠設置最大的連接數量。若是redis是一個公共的redis,多個用戶都可以連接使用,那麼存在多個用戶同時修改一個key的值的狀況,怎麼解決?
1.樂觀鎖:確保同一時間,只能有一個系統實例在操做某個 key,別人都不容許讀和寫。
# TODO 怎麼實行樂觀鎖?
2. 隊列:把redis.set的操做放入一個隊列中,先進先出,一個一個執行。
12. redis資源競爭的問題。
以:redis的優先級隊列爲例:
線程a要執行兩部才能刪除數據:第一步:取數據(取完後數據還在,內存中),第二步:刪除數據1
線程b要執行兩部才能刪除數據:第一步:取數據(取完後數據還在,內存中),第二步:刪除數據1
問題1:線程a執行第一步後,被線程b搶到時間片輪轉,執行完了操做,刪除數據1成功,線程a繼續執行時,數據1已經不存在了。刪除數據1失敗。
問題2:假如存儲的是請求對象呢,一樣的兩個線程會對同一份數據進行兩次操做,會發出兩個一樣的請求,形成資源浪費。
解決方法:
方法一:共享數據同時只能被一個線程進行處理,相似於原子性。
方法二:redis的事務,可是redis的事務也不是百分之百的安全。
內存中的鎖,只能解決單進程中多個線程之間的共享問題:拿到鎖之間的代碼,不能有過多的耗時操做,不然性能會直線降低。
前提是:多個線程共用同一把鎖,才能實現。怎樣保證多線程拿到同一個鎖對象,怎樣去實現?
問題:分佈式形式,不一樣服務器上的線程取搶奪共享鎖,怎麼實現?
redis中的setnx:
127.0.0.1:6379> setnx lock b (integer) 0 127.0.0.1:6379> setnx lock a (integer) 0 127.0.0.1:6379> setnx locking a # locking不存在,添加值,返回1 (integer) 1 127.0.0.1:6379> setnx locking b # locking存在,不添加值,返回0 (integer) 0 127.0.0.1:6379> get locking # 獲取locking中的值 "a" 127.0.0.1:6379>
127.0.0.1:6379> del locking # 刪除locking
(integer) 1
死鎖產生的緣由:1. a 線程在執行完上鎖後,掛掉了,致使其餘線程永遠沒法打開鎖(解鈴還須繫鈴人)2.其餘線程篡改了 locking中的a,改爲a1,那麼線程a永遠沒法打開鎖,其餘線程也永遠沒法進行上鎖,和解鎖操做。
解決死鎖的辦法:1.人爲強行刪除locking,可是不知道什麼時候會死鎖,這個方法時效性比較差 2.設置過時時間,過時後,刪除locking,其餘的線程也就能進行對應的上鎖和解鎖的操做了。
import redis class RedisLock(object): def __init__(self,host="localhost",port=6379,db=0,lock_name): self.redis= redis.StrictRedis(host=host,port=port,db=db) self.lockname = lockname def acquire_lock(self,thread_id): """thread_id線程號,用來進行解鎖""" # 若是lockname存在返回0,不存在返回1 ret = self.redis.setnx(self.lockname,thread_id) if ret == 1: print("{}上鎖成功".format(thread_id)) return True else: print("{}上鎖失敗".format(thread_id)) return False def release_lock(self,thread_id): id = self.redis.get(self.lockname) # 確保解鎖和上鎖線程一致 if id == thread_id: self.redis.delete(self.lockname) print("{}解鎖成功".format(thread_id)) return True else: print("{}解鎖成功".format(thread_id)) return False