NoSQL(Not Only SQL),泛指非關係型的數據庫(mysql、oracle、sqlserver都是關係型數據庫)。html
數據之間無關係,隨意擴展node
數據存儲簡單,能夠存在內存中,讀寫速度快mysql
不須要建表、字段。自定義格式redis
分類算法 |
Examples舉例sql |
典型應用場景數據庫 |
數據模型segmentfault |
優勢數組 |
缺點緩存 |
鍵值(key-value) |
Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB,memcache |
內容緩存,主要用於處理大量數據的高訪問負載,也用於一些日誌系統等等。 |
Key 指向 Value 的鍵值對,一般用hash table來實現 |
查找速度快 |
數據無結構化,一般只被看成字符串或者二進制數據 |
列存儲數據庫 |
Cassandra, HBase, Riak |
分佈式的文件系統 |
以列簇式存儲,將同一列數據存在一塊兒 |
查找速度快,可擴展性強,更容易進行分佈式擴展 |
功能相對侷限 |
文檔型數據庫 |
CouchDB, MongoDb |
Web應用(與Key-Value相似,Value是結構化的,不一樣的是數據庫可以瞭解Value的內容) |
Key-Value對應的鍵值對,Value爲結構化數據 |
數據結構要求不嚴格,表結構可變,不須要像關係型數據庫同樣須要預先定義表結構 |
查詢性能不高,並且缺少統一的查詢語法。 |
圖形數據庫 |
Neo4J, InfoGrid, Infinite Graph |
社交網絡,推薦系統等。專一於構建關係圖譜 |
圖結構 |
利用圖結構相關算法。好比最短路徑尋址,N度關係查找等 |
不少時候須要對整個圖作計算才能得出須要的信息,並且這種結構不太好作分佈式的集羣方案。 |
不須要預約義模式:不須要事先定義數據模式,預約義表結構。數據中的每條記錄均可能有不一樣的屬性和格式。當插入數據時,並不須要預先定義它們的模式。
無共享架構:相對於將全部數據存儲的存儲區域網絡中的全共享架構。NoSQL每每將數據劃分後存儲在各個本地服務器上。由於從本地磁盤讀取數據的性能每每好於經過網絡傳輸讀取數據的性能,從而提升了系統的性能。
彈性可擴展:能夠在系統運行的時候,動態增長或者刪除結點。不須要停機維護,數據能夠自動遷移。
分區:相對於將數據存放於同一個節點,NoSQL數據庫須要將數據進行分區,將記錄分散在多個節點上面。而且一般分區的同時還要作複製。這樣既提升了並行性能,又能保證沒有單點失效的問題。
異步複製:和RAID存儲系統不一樣的是,NoSQL中的複製,每每是基於日誌的異步複製。這樣,數據就能夠儘快地寫入一個節點,而不會被網絡傳輸引發遲延。缺點是並不老是能保證一致性,這樣的方式在出現故障的時候,可能會丟失少許的數據。
BASE:相對於事務嚴格的ACID(原子性,一致性,隔離性,持久性)特性,NoSQL數據庫保證的是BASE(BA——基本可用,S——軟狀態,柔性事務,E——最終一致性)特性。【注】CAP原理(C——一致性,A——可用性,P——分區容錯性,當前NoSQL大部分知足了AP原理)
NoSQL數據庫在如下的這幾種狀況下比較適用:一、數據模型比較簡單;二、須要靈活性更強的IT系統;三、對數據庫性能要求較高;四、不須要高度的數據一致性;五、對於給定key,比較容易映射覆雜值的環境。
MemCache是一個開源的高性能的分佈式的內存對象緩存系統,用於各類動態應用以減輕數據庫負擔。它經過在內存中緩存數據和對象,來減小讀取數據庫的次數,從而提升動態、數據庫驅動應用速度。MemCache會在內存中開闢一塊空間,創建一個統一的巨大的hash表,hash表可以用來存儲各類格式的數據,包括圖像、視頻、文件以及數據庫檢索的結果等。
【注】MemCache 和 MemCached:MemCache是這個項目的名稱,而MemCached是服務器端的主程序名稱。
一般在訪問量高的Web網站和應用中使用MemCache,用來緩解數據庫的壓力,而且提高網站和應用的響應速度。
在應用程序中,咱們一般在如下節點來使用MemCached:
經常使用工做流程(以下圖):
緩存到MemCached中的數據庫數據,在更新數據庫時要同時更新MemCached。
MemCached採用了C/S架構,在Server端啓動後,以守護程序的方式,監聽客戶端的請求。啓動時能夠指定監聽的IP(服務器的內網ip/外網ip)、端口號(因此作分佈式測試時,一臺服務器上能夠啓動多個不一樣端口號的MemCached進程)、使用的內存大小等關鍵參數。一旦啓動,服務就會一直處於可用狀態。
爲了提升性能,MemCached緩存的數據所有存儲在MemCached管理的內存中,因此重啓服務器以後緩存數據會清空,不支持持久化。
1. 內存結構
2. 內存分配方式
MemCache中的value存放位置是由value的大小決定,value會被存放到與chunk大小最接近的一個slab中,好比slab[1]的chunk大小爲88字節、slab[2]的chunk大小爲112字節、slab[3]的chunk大小爲144字節(默認相鄰slab內的chunk基本以1.25爲比例進行增加,MemCache啓動時能夠用-f指定這個比例),那麼一個100字節的value,將被放到2號slab中。
3. 內存回收方式
針對MemCache的內存分配及回收算法,總結三點:
爲了提高MemCached的存儲容量和性能,咱們應用的客戶端可能會對應多個MemCached服務器來提供服務,這就是MemCached的分佈式。
1. 分佈式實現原理
MemCached的目前版本是經過C實現,採用了單進程、多線程、異步I/O,基於事件(event_based)的服務方式.使用libevent做爲事件通知實現。多個Server能夠協同工做,但這些 Server 之間保存的數據各不相同,並且並不通訊(與之造成對比的,好比JBoss Cache,某臺服務器有緩存數據更新時,會通知集羣中其餘機器更新緩存或清除緩存數據),每一個Server只是對本身的數據進行管理。
Client端經過IP地址和端口號指定Server端,將須要緩存的數據是以key->value對的形式保存在Server端。key的值經過hash進行轉換,根據hash值把value傳遞到對應的具體的某個Server上。當須要獲取對象數據時,也根據key進行。首先對key進行hash,經過得到的值能夠肯定它被保存在了哪臺Server上,而後再向該Server發出請求。Client端只須要知道保存hash(key)的值在哪臺服務器上就能夠了。
2. 分佈式算法解析
MemCached網絡模型是典型的單進程多線程模型,採用libevent處理網絡請求,主進程負責將新來的鏈接分配給work線程,work線程負責處理鏈接,有點相似與負載均衡,經過主進程分發到對應的工做線程。
MemCached默認有7個線程,4個主要的工做線程,3個輔助線程,線程可劃分爲如下4種:
Redis是一個key-value存儲系統。和Memcached相似,它支持存儲的value類型相對更多,包括string(字符串)、 list(鏈表)、set(集合)和zset(有序集合)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操做,並且這些操做都是原子性的。在此基礎上,redis支持各類不一樣方式的排序。與memcached同樣,爲了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件,而且在此基礎上實現了master-slave(主從)同步,當前 Redis的應用已經很是普遍,國內像新浪、淘寶,國外像 Flickr、Github等均在使用Redis的緩存服務。
Redis支持豐富的數據類型,並提供了大量簡單高效的功能。Redis的底層數據結構總覽圖:
上圖列出了Redis內部底層的一些重要數據結構,包括List, Set, Hash, String等。
1. Redis Object
redisObject定義了類型,編碼方式,LRU時間,引用計數,*ptr指向實際保存值指針。
type: redisObject的類型,字符串,列表,集合,有序集,哈希表等
encoding: 底層實現結構,字符串,整數,跳躍表,壓縮列表等
ptr:實際指向保存值的數據結構
舉個具體例子,redisObject{type: REDIS_LIST, encoding:REDIS_ENCODING_LINKEDLIST},
這個對象是Redis列表,其值保存在一個鏈表中,ptr指針指向這個列表。Redis本身實現對象管理機制,並基於引用計數的垃圾回收。Redis提供了incrRefCount與decrRefCount來管理對象跟蹤對象的引用, 當減小引用時檢測計數器爲是否須要釋放內存對象。
2. RedisDB
RedisDB內部數據結構,封裝了數據庫層面的信息:
從redisDB來看,幾個重要屬性:
id:數據庫內部編號,僅供內部操做使用,如AOF等
dict:存放整個數據庫的鍵值對,鍵爲字符串,值爲Redis的數據結構,如List, Set, Hash等。
expires:鍵的過時時間
3. RedisServer
RedisServer代碼在Redis 3.0支持Cluster後變得較複雜,整體來講包含以下幾個大部分:
通用部分:如pid進程id,數據庫指針,命令字典表,Sentinel模式標誌位等
網絡信息:如port TCP監聽端口,Cluster Bus監聽socket, 已使用slot數量,Active的客戶端列表, 當前客戶端,Slaves列表等。
其它信息:如AOF信息,統計信息, 配置信息(如已經配置總db數量dbnum等),日誌信息,Replication配置,Pub/Sub, Cluster信息,Lua腳本信息配置等等。
4. Redis Hash
Redis的哈希表/字典是其核心數據結構之一,值得深刻研究。Redis Hash數據結構, Hash新建立時,在不影響效率狀況下,Redis默認使用zipmap做爲底層實現以節省空間,只有當size超出必定限制後(hash-max-zipmap-entries ),Redis纔會自動把zipmap轉換爲下圖Hash Table。
上圖字典的底層實現爲哈希表,每一個字典包含2個哈希表,ht[0], ht[1], 1號哈希表是在rehash過程當中才使用的。而哈希表則由dictEntry構成。
每一個字典包含了3個內部數據結構:
Dict:字典的根結構,包含了2個dictht,其中2做爲rehashing之用
Dictht:包含了linkedlist dictEntry
DictEntry:包含了3個數據結構(double/uint64_6/int64t)的鏈表,相似Java HashMap中的Entry結構
5. Hash算法
目前Redis中引入了一些經典哈希算法,而HashTable則主要爲如下兩種:
MurmurHash2 32bit算法:著名的非加密型哈希函數,能產生32位或64位哈希值,最新版本爲MurmurHash3。該算法針對一個字符串進行哈希,可表現較強離散性。
基於djb算法實現散列算法:該算法較爲簡單,一樣是將字符串轉換爲哈希值。主要利用字符串中的ASCII碼與一個隨機seed,進行變換獲得哈希值。
評估一個哈希算法的優劣,主要看其哈希值的離散均勻效果以及消除衝突程度。
6. Rehash
相似Java中的HashMap, 當有新鍵值對添加到Redis字典時,有可能會觸發rehash。Redis中處理哈希碰撞的方法與Java同樣,都是採用鏈表法,整個哈希表的性能則依賴於它的大小size和它已經保存節點數量used的比率。比率在1:1時,哈希表的性能最好,若是節點數量比哈希表大小大不少的話,則整個哈希表就退化成多個鏈表,其性能優點全無。
上圖的哈希表,平均每次失敗查找須要訪問5個節點。爲了保持高效性能,在不修改鍵值對狀況下,須要進行rehash,目標是將ratio比率維持在1:1左右。Ratio = Used / Size
rehash觸發條件:
天然rehash:ratio >= 1, 且變量dict_can_resize爲真
強制rehash:ratio大於dict_force_resize_ratio(v 3.2.1版本爲5)
rehash執行過程:
建立ht[1]並分配至少2倍於ht[0] table的空間
將ht[0] table中的全部鍵值對遷移到ht[1] table
將ht[0]數據清空,並將ht[1]替換爲新的ht[0]
Redis哈希爲了不整個rehash過程當中服務被阻塞,採用了漸進式的rehash,即rehash程序激活後,並非立刻執行直到完成,而是分屢次,漸進式(incremental)的完成。同時,爲了保證併發安全,在執行rehash中間執行添加時,新的節點會直接添加到ht[1]而不是ht[0], 這樣保證了數據的完整性與安全性。另外一方面,哈希的Rehash在還提供了創新的(相對於Java HashMap)收縮(shrink)字典,當可用節點遠遠大於已用節點的時候,rehash會自動進行收縮,具體過程與上面相似以保證比率始終高效使用。
持久化簡單來說就是將數據放到斷電後數據不會丟失的設備中,也就是咱們一般理解的硬盤上。
首先咱們來看一下數據庫在進行寫操做時到底作了哪些事,主要有下面五個過程:
經過上面5步的瞭解,可能咱們會但願搞清下面一些問題:
數據損壞
所謂數據損壞,就是數據沒法恢復,上面咱們講的都是如何保證數據是確實寫到磁盤上去,可是寫到磁盤上可能並不意味着數據不會損壞。好比咱們可能一次寫請求會進行兩次不一樣的寫操做,當意外發生時,可能會致使一次寫操做安全完成,可是另外一次尚未進行。若是數據庫的數據文件結構組織不合理,可能就會致使數據徹底不能恢復的情況出現。
這裏一般也有三種策略來組織數據,以防止數據文件損壞到沒法恢復的狀況:
RDB持久化是指在指定的時間間隔內將內存中的數據集快照寫入磁盤。
也是默認的持久化方式,這種方式是就是將內存中數據以快照的方式寫入到二進制文件中,默認的文件名爲dump.rdb。
能夠經過配置設置自動作快照持久化的方式。咱們能夠配置redis在n秒內若是超過m個key被修改就自動作快照,下面是默認的快照保存配置
save 900 1 #900秒內若是超過1個key被修改,則發起快照保存
save 300 10 #300秒內容如超過10個key被修改,則發起快照保存
save 60 10000
client 也能夠使用save或者bgsave命令通知redis作一次快照持久化。save操做是在主線程中保存快照的,因爲redis是用一個主線程來處理全部 client的請求,這種方式會阻塞全部client請求。因此不推薦使用。
另外一點須要注意的是,每次快照持久化都是將內存數據完整寫入到磁盤一次,並非增量的只同步髒數據。若是數據量大的話,並且寫操做比較多,必然會引發大量的磁盤io操做,可能會嚴重影響性能。
一旦採用該方式,那麼你的整個Redis數據庫將只包含一個文件,這樣很是方便進行備份。好比你可能打算沒1天歸檔一些數據。
redis會將每個收到的寫命令都經過write函數追加到文件中(默認是 appendonly.aof)。
當redis重啓時會經過從新執行文件中保存的寫命令來在內存中重建整個數據庫的內容。固然因爲os會在內核中緩存 write作的修改,因此可能不是當即寫到磁盤上。這樣aof方式的持久化也仍是有可能會丟失部分修改。不過咱們能夠經過配置文件告訴redis咱們想要 經過fsync函數強制os寫入到磁盤的時機。有三種方式以下(默認是:每秒fsync一次)
appendonly yes //啓用aof持久化方式
# appendfsync always //每次收到寫命令就當即強制寫入磁盤,最慢的,可是保證徹底的持久化,不推薦使用
# appendfsync everysec //每秒鐘強制寫入磁盤一次,在性能和持久化方面作了很好的折中,推薦
# appendfsync no //徹底依賴os,性能最好,持久化沒保證
aof 的方式也同時帶來了另外一個問題。持久化文件會變的愈來愈大。例如咱們調用incr test命令100次,文件中必須保存所有的100條命令,其實有99條都是多餘的。由於要恢復數據庫的狀態其實文件中保存一條set test 100就夠了。
爲了壓縮aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis將使用與快照相似的方式將內存中的數據 以命令的方式保存到臨時文件中,最後替換原來的文件。具體過程以下
須要注意到是重寫aof文件的操做,並無讀取舊的aof文件,而是將整個內存中的數據庫內容用命令的方式重寫了一個新的aof文件,這點和快照有點相似。
使用 AOF 持久化會讓 Redis 變得很是耐久(much more durable):你能夠設置不一樣的 fsync 策略,好比無 fsync ,每秒鐘一次 fsync ,或者每次執行寫入命令時 fsync 。 AOF 的默認策略爲每秒鐘 fsync 一次,在這種配置下,Redis 仍然能夠保持良好的性能,而且就算髮生故障停機,也最多隻會丟失一秒鐘的數據( fsync 會在後臺線程執行,因此主線程能夠繼續努力地處理命令請求)。
AOF 文件是一個只進行追加操做的日誌文件(append only log), 所以對 AOF 文件的寫入不須要進行 seek , 即便日誌由於某些緣由而包含了未寫入完整的命令(好比寫入時磁盤已滿,寫入中途停機,等等), redis-check-aof 工具也能夠輕易地修復這種問題。
Redis 能夠在 AOF 文件體積變得過大時,自動地在後臺對 AOF 進行重寫: 重寫後的新 AOF 文件包含了恢復當前數據集所需的最小命令集合。 整個重寫操做是絕對安全的,由於 Redis 在建立新 AOF 文件的過程當中,會繼續將命令追加到現有的 AOF 文件裏面,即便重寫過程當中發生停機,現有的 AOF 文件也不會丟失。 而一旦新 AOF 文件建立完畢,Redis 就會從舊 AOF 文件切換到新 AOF 文件,並開始對新 AOF 文件進行追加操做。
AOF 文件有序地保存了對數據庫執行的全部寫入操做, 這些寫入操做以 Redis 協議的格式保存, 所以 AOF 文件的內容很是容易被人讀懂, 對文件進行分析(parse)也很輕鬆。 導出(export) AOF 文件也很是簡單: 舉個例子, 若是你不當心執行了 FLUSHALL 命令, 但只要 AOF 文件未被重寫, 那麼只要中止服務器, 移除 AOF 文件末尾的 FLUSHALL 命令, 並重啓 Redis , 就能夠將數據集恢復到 FLUSHALL 執行以前的狀態。
對於相同的數據集來講,AOF 文件的體積一般要大於 RDB 文件的體積。
根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB 。 在通常狀況下, 每秒 fsync 的性能依然很是高, 而關閉 fsync 可讓 AOF 的速度和 RDB 同樣快, 即便在高負荷之下也是如此。 不過在處理巨大的寫入載入時,RDB 能夠提供更有保證的最大延遲時間(latency)。
AOF 在過去曾經發生過這樣的 bug : 由於個別命令的緣由,致使 AOF 文件在從新載入時,沒法將數據集恢復成保存時的原樣。 (舉個例子,阻塞命令 BRPOPLPUSH 就曾經引發過這樣的 bug 。) 測試套件裏爲這種狀況添加了測試: 它們會自動生成隨機的、複雜的數據集, 並經過從新載入這些數據來確保一切正常。 雖然這種 bug 在 AOF 文件中並不常見, 可是對比來講, RDB 幾乎是不可能出現這種 bug 的。
數據類型與操做:Redis擁有更多豐富的數據結構支持與操做,而Memcached則需客戶端本身處理並進行網絡交互
內存使用率:簡單K/V存儲,Memcached內存利用率更高(使用了slab與大小不一樣的chunk來管理內存),而若是採用Redis Hash來存儲則其組合壓縮,內存利用率高於Memcached
性能:整體來講,兩者性能接近;Redis使用了單核(單線程IO複用,封裝了AeEvent事件處理框架,實現了epoll,kqueue,select),Memcached採用了多核,各有利弊;當數據大於100K的時候,Memcached性能高於Redis
數據持久化:Redis支持數據文件持久化,RDB與AOF兩種策略;Memcached則不支持
分佈式:Memcached自己並不支持服務器端分佈式,客戶端只能藉助一致性哈希分佈式算法來實現Memcached分佈式存儲;固然Redis也是從3.0版本開始才支持服務器端cluster的,重要的是如今支持了。
其餘方面:Redis提供其餘一些功能,如Pub/Sub, Queue, 簡單Transacation, Replication等。
HashMap單機受限於內存容量,而正是Redis分佈式之優點
HashMap當數據量超過必定限制後,須要妥善管理堆內存,否則會形成內存溢出或者Memory Leak;Redis則具有了文件持久性,以及Failover達到HA.
HashMap只能受限於本機,而Redis天生分佈式,可讓多個App Server訪問,負載均衡。
因此Redis適合所有數據都在內存的場景包括須要臨時持久化,尤爲做爲緩存來使用,並支持對緩存數據進行簡單處理計算;如涉及Redis與RDBMS雙向同步的話,則須要引入一些複雜度。
Reids Sentinel誕生於2012年(Redis 2.4版本),建議在單機Redis或者客戶端模式Cluster的時候(非 3.0版本Redis Cluster)採用,做爲HA, Failover來使用。
Sentinel主要提供了集羣管理,包括監控,通知,自動故障恢復。如上圖,當其中一個master沒法正常工做時,Sentinel將把一個Slave提高爲Master, 從而自動恢復故障。而Sentinel自己也作到了分佈式,能夠部署多個Sentinel實例來監控Redis實例(建議基數,至少3個Sentinel實例來監控一組Redis Master/Slaves),多個Sentinel進程間經過Gossip協議來肯定Master是否宕機,經過Agreement協議來決定是否執行故障自動遷移以及從新選主,總體設計相似ZooKeeper)。
所謂分佈式即支持數據分片,而且自動管理故障恢復(failover)與備份(replication)。
如上圖Redis Cluster採用了無中心結構,每一個節點都保存,共享數據和集羣狀態,每一個節點與其它全部節點通訊,使用Gossip協議來傳播及發現新節點,經過分區來提供必定程度可用性,當某個node的Master宕機時,Cluster會自動選舉一個Slave造成一個新的Master,這裏應該是
借鑑,重用了Sentinel的功能。另外,Redis Cluster並無使用一般的一致性哈希, 而引入哈希槽的概念,Cluster中固定有16384個slot, 每一個key經過CRC16校驗後對16384取模來決定其對應slot的位置,而每一個node負責一部分的slot管理,當node變化時,動態調整slot的分佈,而數據則無須挪動。對於客戶端來講,client能夠向任意一個實例請求,Cluster會自動定位須要訪問的slot。
上圖查詢路由過程當中,咱們隨機發送到任意一個Redis實例,這個實例會按照上文提到的CRC16校驗後取模定位,並轉發至正確的Redis實例中。
https://blog.csdn.net/cangchen/article/details/44830087
https://segmentfault.com/a/1190000012950110
https://www.cnblogs.com/work115/p/5584646.html
https://blog.csdn.net/erixhao/article/details/52223839
https://blog.csdn.net/lhx13636332274/article/details/73867165