Redis 分布式集群的几种方案及问题
1、Redis 分布式集群的幾種方案
1.1、主從復(fù)制
從服務(wù)器連接主服務(wù)器,發(fā)送SYNC命令; 主服務(wù)器接收到SYNC命名后,開始執(zhí)行BGSAVE命令生成RDB文件并使用緩沖區(qū)記錄此后執(zhí)行的所有寫命令; 主服務(wù)器BGSAVE執(zhí)行完后,向所有從服務(wù)器發(fā)送快照文件,并在發(fā)送期間繼續(xù)記錄被執(zhí)行的寫命令; 從服務(wù)器收到快照文件后丟棄所有舊數(shù)據(jù),載入收到的快照; 主服務(wù)器快照發(fā)送完畢后開始向從服務(wù)器發(fā)送緩沖區(qū)中的寫命令; 從服務(wù)器完成對快照的載入,開始接收命令請求,并執(zhí)行來自主服務(wù)器緩沖區(qū)的寫命令;(從服務(wù)器初始化完成)主服務(wù)器每執(zhí)行一個寫命令就會向從服務(wù)器發(fā)送相同的寫命令,從服務(wù)器接收并執(zhí)行收到的寫命令(從服務(wù)器初始化完成后的操作)
主從復(fù)制優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
支持主從復(fù)制,主機(jī)會自動將數(shù)據(jù)同步到從機(jī),可以進(jìn)行讀寫分離為了分載Master的讀操作壓力,Slave服務(wù)器可以為客戶端提供只讀操作的服務(wù),寫服務(wù)仍然必須由Master來完成Slave同樣可以接受其它Slaves的連接和同步請求,這樣可以有效的分載Master的同步壓力。Master Server是以非阻塞的方式為Slaves提供服務(wù)。所以在Master-Slave同步期間,客戶端仍然可以提交查詢或修改請求。Slave Server同樣是以非阻塞的方式完成數(shù)據(jù)同步。在同步期間,如果有客戶端提交查詢請求,Redis則返回同步之前的數(shù)據(jù)
缺點(diǎn):
Redis不具備自動容錯和恢復(fù)功能,主機(jī)從機(jī)的宕機(jī)都會導(dǎo)致前端部分讀寫請求失敗,需要等待機(jī)器重啟或者手動切換前端的IP才能恢復(fù)。主機(jī)宕機(jī),宕機(jī)前有部分?jǐn)?shù)據(jù)未能及時同步到從機(jī),切換IP后還會引入數(shù)據(jù)不一致的問題,降低了系統(tǒng)的可用性。Redis較難支持在線擴(kuò)容,在集群容量達(dá)到上限時在線擴(kuò)容會變得很復(fù)雜。
1.2、 哨兵模式
當(dāng)主服務(wù)器中斷服務(wù)后,可以將一個從服務(wù)器升級為主服務(wù)器,以便繼續(xù)提供服務(wù),但是這個過程需要人工手動來操作。 為此,Redis 2.8中提供了哨兵工具來實(shí)現(xiàn)自動化的系統(tǒng)監(jiān)控和故障恢復(fù)功能。
哨兵的作用就是監(jiān)控Redis系統(tǒng)的運(yùn)行狀況。它的功能包括以下兩個。
(1)監(jiān)控主服務(wù)器和從服務(wù)器是否正常運(yùn)行。
(2)主服務(wù)器出現(xiàn)故障時自動將從服務(wù)器轉(zhuǎn)換為主服務(wù)器。
哨兵的工作方式:
每個Sentinel(哨兵)進(jìn)程以每秒鐘一次的頻率向整個集群中的Master主服務(wù)器,Slave從服務(wù)器以及其他Sentinel(哨兵)進(jìn)程發(fā)送一個 PING 命令。如果一個實(shí)例(instance)距離最后一次有效回復(fù) PING 命令的時間超過 down-after-milliseconds 選項所指定的值, 則這個實(shí)例會被 Sentinel(哨兵)進(jìn)程標(biāo)記為主觀下線(SDOWN)如果一個Master主服務(wù)器被標(biāo)記為主觀下線(SDOWN),則正在監(jiān)視這個Master主服務(wù)器的所有 Sentinel(哨兵)進(jìn)程要以每秒一次的頻率確認(rèn)Master主服務(wù)器的確進(jìn)入了主觀下線狀態(tài)當(dāng)有足夠數(shù)量的 Sentinel(哨兵)進(jìn)程(大于等于配置文件指定的值)在指定的時間范圍內(nèi)確認(rèn)Master主服務(wù)器進(jìn)入了主觀下線狀態(tài)(SDOWN), 則Master主服務(wù)器會被標(biāo)記為客觀下線(ODOWN)在一般情況下, 每個 Sentinel(哨兵)進(jìn)程會以每 10 秒一次的頻率向集群中的所有Master主服務(wù)器、Slave從服務(wù)器發(fā)送 INFO 命令。當(dāng)Master主服務(wù)器被 Sentinel(哨兵)進(jìn)程標(biāo)記為客觀下線(ODOWN)時,Sentinel(哨兵)進(jìn)程向下線的 Master主服務(wù)器的所有 Slave從服務(wù)器發(fā)送 INFO 命令的頻率會從 10 秒一次改為每秒一次。若沒有足夠數(shù)量的 Sentinel(哨兵)進(jìn)程同意 Master主服務(wù)器下線, Master主服務(wù)器的客觀下線狀態(tài)就會被移除。若 Master主服務(wù)器重新向 Sentinel(哨兵)進(jìn)程發(fā)送 PING 命令返回有效回復(fù),Master主服務(wù)器的主觀下線狀態(tài)就會被移除。
哨兵模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
哨兵模式是基于主從模式的,所有主從的優(yōu)點(diǎn),哨兵模式都具有。
主從可以自動切換,系統(tǒng)更健壯,可用性更高。
缺點(diǎn):
Redis較難支持在線擴(kuò)容,在集群容量達(dá)到上限時在線擴(kuò)容會變得很復(fù)雜。
1.3、Redis-Cluster集群
redis的哨兵模式基本已經(jīng)可以實(shí)現(xiàn)高可用,讀寫分離 ,但是在這種模式下每臺redis服務(wù)器都存儲相同的數(shù)據(jù),很浪費(fèi)內(nèi)存,所以在redis3.0上加入了cluster模式,實(shí)現(xiàn)的redis的分布式存儲,也就是說每臺redis節(jié)點(diǎn)上存儲不同的內(nèi)容。
Redis-Cluster采用無中心結(jié)構(gòu),它的特點(diǎn)如下:
所有的redis節(jié)點(diǎn)彼此互聯(lián)(PING-PONG機(jī)制),內(nèi)部使用二進(jìn)制協(xié)議優(yōu)化傳輸速度和帶寬。
節(jié)點(diǎn)的fail是通過集群中超過半數(shù)的節(jié)點(diǎn)檢測失效時才生效。
客戶端與redis節(jié)點(diǎn)直連,不需要中間代理層.客戶端不需要連接集群所有節(jié)點(diǎn),連接集群中任何一個可用節(jié)點(diǎn)即可。
工作方式:
在redis的每一個節(jié)點(diǎn)上,都有這么兩個東西,一個是插槽(slot),它的的取值范圍是:0-16383。還有一個就是cluster,可以理解為是一個集群管理的插件。當(dāng)我們的存取的key到達(dá)的時候,redis會根據(jù)crc16的算法得出一個結(jié)果,然后把結(jié)果對 16384 求余數(shù),這樣每個 key 都會對應(yīng)一個編號在 0-16383 之間的哈希槽,通過這個值,去找到對應(yīng)的插槽所對應(yīng)的節(jié)點(diǎn),然后直接自動跳轉(zhuǎn)到這個對應(yīng)的節(jié)點(diǎn)上進(jìn)行存取操作。
為了保證高可用,redis-cluster集群引入了主從模式,一個主節(jié)點(diǎn)對應(yīng)一個或者多個從節(jié)點(diǎn),當(dāng)主節(jié)點(diǎn)宕機(jī)的時候,就會啟用從節(jié)點(diǎn)。當(dāng)其它主節(jié)點(diǎn)ping一個主節(jié)點(diǎn)A時,如果半數(shù)以上的主節(jié)點(diǎn)與A通信超時,那么認(rèn)為主節(jié)點(diǎn)A宕機(jī)了。如果主節(jié)點(diǎn)A和它的從節(jié)點(diǎn)A1都宕機(jī)了,那么該集群就無法再提供服務(wù)了。
2、Redis 和數(shù)據(jù)庫同步、緩沖穿透、雪崩問題、hyperloglog slowqery 實(shí)現(xiàn)原理?
2.1、Redis數(shù)據(jù)庫同步
捕捉所有mysql的修改,寫入和刪除事件,對redis進(jìn)行操作
2.2、緩沖穿透
用戶想要查詢一個數(shù)據(jù),發(fā)現(xiàn)redis內(nèi)存數(shù)據(jù)庫沒有,也就是緩存沒有命中,于是向持久層數(shù)據(jù)庫查詢。發(fā)現(xiàn)也沒有,于是本次查詢失敗。當(dāng)用戶很多的時候,緩存都沒有命中,于是都去請求了持久層數(shù)據(jù)庫。這會給持久層數(shù)據(jù)庫造成很大的壓力,這時候就相當(dāng)于出現(xiàn)了緩存穿透。
2.3、雪崩
緩存雪崩是指,緩存層出現(xiàn)了錯誤,不能正常工作了。于是所有的請求都會達(dá)到存儲層,存儲層的調(diào)用量會暴增,造成存儲層也會掛掉的情況。
2.4、hyperloglog
優(yōu)點(diǎn):在輸入元素的數(shù)量或者體積非常非常大時,計算基數(shù)所需的空間總是固定 的、并且是很小的。(在 Redis 里面,每個 HyperLogLog 鍵只需要花費(fèi) 12 KB 內(nèi)存,就可以計算接近 2^64 個不同元素的基 數(shù)。這和計算基數(shù)時,元素越多耗費(fèi)內(nèi)存就越多的集合形成鮮明對比。)缺點(diǎn): HyperLogLog 只會根據(jù)輸入元素來計算基數(shù),而不會儲存輸入元素本身,所以 HyperLogLog 不能像集合那樣,返回輸入的各個元素。
3、100W并發(fā)4G數(shù)據(jù),10W并發(fā)400G數(shù)據(jù),如何設(shè)計Redis存儲方式?
淘汰策略
存儲吃緊的一個重要原因在于每天會有很多新數(shù)據(jù)入庫,所以及時清理數(shù)據(jù)尤為重要。主要方法就是發(fā)現(xiàn)和保留熱數(shù)據(jù)淘汰冷數(shù)據(jù)。
網(wǎng)民的量級遠(yuǎn)遠(yuǎn)達(dá)不到幾十億的規(guī)模,id有一定的生命周期,會不斷的變化。所以很大程度上我們存儲的id實(shí)際上是無效的。而查詢其實(shí)前端的邏輯就是廣告曝光,跟人的行為有關(guān),所以一個id在某個時間窗口的(可能是一個campaign,半個月、幾個月)訪問行為上會有一定的重復(fù)性。
數(shù)據(jù)初始化之前,我們先利用hbase將日志的id聚合去重,劃定TTL的范圍,一般是35天,這樣可以砍掉近35天未出現(xiàn)的id。另外在Redis中設(shè)置過期時間是35天,當(dāng)有訪問并命中時,對key進(jìn)行續(xù)命,延長過期時間,未在35天出現(xiàn)的自然淘汰。這樣可以針對穩(wěn)定cookie或id有效,實(shí)際證明,續(xù)命的方法對idfa和imei比較實(shí)用,長期積累可達(dá)到非常理想的命中。
減少膨脹
Hash表空間大小和Key的個數(shù)決定了沖突率(或者用負(fù)載因子衡量),再合理的范圍內(nèi),key越多自然hash表空間越大,消耗的內(nèi)存自然也會很大。再加上大量指針本身是長整型,所以內(nèi)存存儲的膨脹十分可觀。先來談?wù)勅绾伟裬ey的個數(shù)減少。
大家先來了解一種存儲結(jié)構(gòu)。我們期望將key1=>value1存儲在redis中,那么可以按照如下過程去存儲。先用固定長度的隨機(jī)散列md5(key)值作為redis的key,我們稱之為BucketId,而將key1=>value1存儲在hashmap結(jié)構(gòu)中,這樣在查詢的時候就可以讓client按照上面的過程計算出散列,從而查詢到value1。
過程變化簡單描述為:get(key1) -> hget(md5(key1), key1) 從而得到value1。
如果我們通過預(yù)先計算,讓很多key可以在BucketId空間里碰撞,那么可以認(rèn)為一個BucketId下面掛了多個key。比如平均每個BucketId下面掛10個key,那么理論上我們將會減少超過90%的redis key的個數(shù)。
具體實(shí)現(xiàn)起來有一些麻煩,而且用這個方法之前你要想好容量規(guī)模。我們通常使用的md5是32位的hexString(16進(jìn)制字符),它的空間是128bit,這個量級太大了,我們需要存儲的是百億級,大約是33bit,所以我們需要有一種機(jī)制計算出合適位數(shù)的散列,而且為了節(jié)約內(nèi)存,我們需要利用全部字符類型(ASCII碼在0~127之間)來填充,而不用HexString,這樣Key的長度可以縮短到一半。
下面是具體的實(shí)現(xiàn)方式
public static byte [] getBucketId(byte [] key, Integer bit) {MessageDigest mdInst = MessageDigest.getInstance("MD5");mdInst.update(key);byte [] md = mdInst.digest();byte [] r = new byte[(bit-1)/7 + 1];// 因?yàn)橐粋€字節(jié)中只有7位能夠表示成單字符int a = (int) Math.pow(2, bit%7)-2;md[r.length-1] = (byte) (md[r.length-1] & a);System.arraycopy(md, 0, r, 0, r.length);for(int i=0;i<r.length;i++) {if(r[i]<0) r[i] &= 127;}return r; } ?參數(shù)bit決定了最終BucketId空間的大小,空間大小集合是2的整數(shù)冪次的離散值。這里解釋一下為何一個字節(jié)中只有7位可用,是因?yàn)閞edis存儲key時需要是ASCII(0~127),而不是byte array。如果規(guī)劃百億級存儲,計劃每個桶分擔(dān)10個kv,那么我們只需2^30=1073741824的桶個數(shù)即可,也就是最終key的個數(shù)。
減少碎片
碎片主要原因在于內(nèi)存無法對齊、過期刪除后,內(nèi)存無法重新分配。通過上文描述的方式,我們可以將人口標(biāo)簽和mapping數(shù)據(jù)按照上面的方式去存儲,這樣的好處就是redis key是等長的。另外對于hashmap中的key我們也做了相關(guān)優(yōu)化,截取cookie或者deviceid的后六位作為key,這樣也可以保證內(nèi)存對齊,理論上會有沖突的可能性,但在同一個桶內(nèi)后綴相同的概率極低(試想id幾乎是隨機(jī)的字符串,隨意10個由較長字符組成的id后綴相同的概率*桶樣本數(shù)=發(fā)生沖突的期望值<<0.05,也就是說出現(xiàn)一個沖突樣本則是極小概率事件,而且這個概率可以通過調(diào)整后綴保留長度控制期望值)。而value只存儲age、gender、geo的編碼,用三個字節(jié)去存儲。
另外提一下,減少碎片還有個很low但是有效的方法,將slave重啟,然后強(qiáng)制的failover切換主從,這樣相當(dāng)于給master整理的內(nèi)存的碎片。
推薦Google-tcmalloc, facebook-jemalloc內(nèi)存分配,可以在value不大時減少內(nèi)存碎片和內(nèi)存消耗。有人測過大value情況下反而libc更節(jié)約。
md5散列桶的方法需要注意的問題
1)kv存儲的量級必須事先規(guī)劃好,浮動的范圍大概在桶個數(shù)的十到十五倍,比如我就想存儲百億左右的kv,那么最好選擇30bit31bit作為桶的個數(shù)。也就是說業(yè)務(wù)增長在一個合理的范圍(1015倍的增長)是沒問題的,如果業(yè)務(wù)太多倍數(shù)的增長,會導(dǎo)致hashset增長過快導(dǎo)致查詢時間增加,甚至觸發(fā)zip-list閾值,導(dǎo)致內(nèi)存急劇上升。
2)適合短小value,如果value太大或字段太多并不適合,因?yàn)檫@種方式必須要求把value一次性取出,比如人口標(biāo)簽是非常小的編碼,甚至只需要3、4個bit(位)就能裝下。
3)典型的時間換空間的做法,由于我們的業(yè)務(wù)場景并不是要求在極高的qps之下,一般每天億到十億級別的量,所以合理利用CPU租值,也是十分經(jīng)濟(jì)的。
4)由于使用了信息摘要降低了key的大小以及約定長度,所以無法從redis里面random出key。如果需要導(dǎo)出,必須在冷數(shù)據(jù)中導(dǎo)出。
5)expire需要自己實(shí)現(xiàn),目前的算法很簡單,由于只有在寫操作時才會增加消耗,所以在寫操作時按照一定的比例抽樣,用HLEN命中判斷是否超過15個entry,超過才將過期的key刪除,TTL的時間戳存儲在value的前32bit中。
6)桶的消耗統(tǒng)計是需要做的。需要定期清理過期的key,保證redis的查詢不會變慢。
4、Redis消息隊列消息實(shí)現(xiàn)原理是什么?
redis設(shè)計用來做緩存的,但是由于它自身的某種特性使得它可以用來做消息隊列,它有幾個阻塞式的API可以使用,正是這些阻塞式的API讓其有能力做消息隊列;另外,做消息隊列的其他特性例如FIFO(先入先出)也很容易實(shí)現(xiàn),只需要一個list對象從頭取數(shù)據(jù),從尾部塞數(shù)據(jù)即可;redis能做消息隊列還得益于其list對象blpop brpop接口以及Pub/Sub(發(fā)布/訂閱)的某些接口,它們都是阻塞版的,所以可以用來做消息隊列。
5、兩個線程交替打印0~100的奇偶
using System; using System.Threading; namespace LeeCarry {public class Test{public static EventWaitHandle oddFlag=new EventWaitHandle(false,EventResetMode.AutoReset);public static EventWaitHandle evenFlag=new EventWaitHandle(false,EventResetMode.AutoReset);public static void Main(string[] args){Thread oddThread=new Thread(OddThread);Thread evenThread=new Thread(EvenThread);evenThread.Start(); oddThread.Start();}private static void EvenThread(){for(int i=0;i<=100;i+=2){Console.WriteLine("線程1:{0}",i);oddFlag.Set();evenFlag.WaitOne();}oddFlag.Set();//最后開一次綠燈防止線程一直被阻塞,也可以在waitone加時間參數(shù)}private static void OddThread(){oddFlag.WaitOne();//確保偶數(shù)線程先運(yùn)行for(int i=1;i<=100;i+=2){Console.WriteLine("線程2:{0}",i);evenFlag.Set();oddFlag.WaitOne();}evenFlag.Set();//最后開一次綠燈防止線程一直被阻塞,也可以在waitone加時間參數(shù)}} } ?6、高并發(fā)系統(tǒng),一般需要怎么做
分布式
提升并發(fā)的最好的辦法,便是提升硬件。舉個大家都熟悉的例子,十年前的諾基亞手機(jī),一般我們只能簡單的掛一個QQ后臺,多干幾個事情,就不行了。五年前,我們用的安卓手機(jī)能開十來個任務(wù),切換也比較流暢了,而今天,剛剛發(fā)布的蘋果iPhone11,性能就更加強(qiáng)勁。但是我們也發(fā)現(xiàn),這兩年,好像手機(jī)的性能沒有飛速發(fā)展了。無論是蘋果、高通還是華為,或者是PC芯片的廠商因特爾或者AMD,都開始慢慢在擠牙膏了。這其實(shí)是受到物理定理的制約,晶體管不可能無限小,無限集成,硬件不可能一直保持突飛猛進(jìn)。并且,越是高端的機(jī)器,成本越貴,并且這個價格很可能是指數(shù)級增長的。谷歌公司在很早之前就發(fā)現(xiàn),于是開始組建分布式系統(tǒng),使用一個集群而不是一臺機(jī)器來完成相關(guān)的工作,憑借這一點(diǎn),谷歌在互聯(lián)網(wǎng)早期迅速發(fā)展。緩存
緩存,是解決高并發(fā)問題的另一個有效手段。因?yàn)榇疟P的讀寫速度較慢,所以我們常常用讀寫速度的更高的內(nèi)存來防止流量到達(dá)磁盤。一般我們會把一些靜態(tài)資源都放在緩存上,或者將一些動態(tài)的又不怎么重要的更新頻率可以接受延遲的放在緩存里。舉個例子,音樂服務(wù)器,我們可以把專輯的圖片、音樂文件這些放在CDN等緩存服務(wù)上,對于一些熱門的評論列表,我們也可以進(jìn)行緩存,一定時間才刷新一次,可以大大減少磁盤的壓力。當(dāng)然,有時候有緩存還遠(yuǎn)遠(yuǎn)不夠,例如前幾天周杰倫的新專輯照樣打垮了QQ音樂的服務(wù)器。異步
即便是有緩存,有些請求仍然沒有辦法快速的響應(yīng)。有些請求是寫請求,舉個例子,沙茶敏寫了一份電子郵件,群發(fā)了1萬個人,群發(fā)的人數(shù)非常多,服務(wù)器要往很多人的信箱投遞消息,假設(shè)一個人需要0.1秒,1萬個人也要1000秒。雖然可以并發(fā)到多臺機(jī)器解決,但是非常浪費(fèi)資源,如果很多人這么做,系統(tǒng)壓力非常大。另外的情況,是有可能某個系統(tǒng)處理非常慢,這個系統(tǒng)既有可能是業(yè)務(wù)非常復(fù)雜,也有可能是第三方系統(tǒng),舉個例子,沙茶敏從支付寶提取一筆資金到某小銀行,因?yàn)榧夹g(shù)原因,某個小銀行每次接口訪問都要10秒鐘,不可能在轉(zhuǎn)賬頁面卡10秒,所以支付寶先告訴用戶轉(zhuǎn)賬成功了,然后異步進(jìn)行。異步,我們通常采用了異步隊列,異步的好處除了削峰,限流,提升用戶體驗(yàn),還能很好的保護(hù)系統(tǒng)。
7、分布式系統(tǒng)怎么做服務(wù)治理
針對互聯(lián)網(wǎng)業(yè)務(wù)的特點(diǎn),eg 突發(fā)的流量高峰、網(wǎng)絡(luò)延時、機(jī)房故障等,重點(diǎn)針對大規(guī)模跨機(jī)房的海量服務(wù)進(jìn)行運(yùn)行態(tài)治理,保障線上服務(wù)的高SLA,滿足用戶的體驗(yàn),常用的策略包括限流降級、服務(wù)嵌入遷出、服務(wù)動態(tài)路由和灰度發(fā)布等
8、對分布式事務(wù)的理解
本質(zhì)上來說,分布式事務(wù)就是為了保證不同數(shù)據(jù)庫的數(shù)據(jù)一致性。
事務(wù)的ACID特性 原子性 一致性 隔離性 持久性
消息事務(wù)+最終一致性
CC提供了一個編程框架,將整個業(yè)務(wù)邏輯分為三塊:Try、Confirm和Cancel三個操作。以在線下單為例,Try階段會去扣庫存,Confirm階段則是去更新訂單狀態(tài),如果更新訂單失敗,則進(jìn)入Cancel階段,會去恢復(fù)庫存。總之,TCC就是通過代碼人為實(shí)現(xiàn)了兩階段提交,不同的業(yè)務(wù)場景所寫的代碼都不一樣,復(fù)雜度也不一樣,因此,這種模式并不能很好地被復(fù)用。
9、如何實(shí)現(xiàn)負(fù)載均衡,有哪些算法可以實(shí)現(xiàn)?
經(jīng)常會用到以下四種算法:隨機(jī)(random)、輪訓(xùn)(round-robin)、一致哈希(consistent-hash)和主備(master-slave)。
10、分布式集群下如何做到唯一序列號
Redis生成ID 這主要依賴于Redis是單線程的,所以也可以用生成全局唯一的ID。可以用Redis的原子操作 INCR和INCRBY來實(shí)現(xiàn)。
11、.net 多線程的四種實(shí)現(xiàn)方式
11.1、Thread類創(chuàng)建多線程
/// <summary> /// Thread類啟動 /// </summary> public static void Thread_Start() {Thread thread = new Thread(new ParameterizedThreadStart(AddA));thread.Start("Thread"); }11.2、委托方式創(chuàng)建多線程
delegate void Delegate_Add(Object stateInfo); /// <summary> /// 委托方式啟動 /// </summary> public static void Delegate_Start() {Delegate_Add dele_add = AddA;dele_add.BeginInvoke("Delegate", null,null); } ?11.3、ThreadPool類創(chuàng)建多線程
/// <summary> /// 線程池方式啟動 /// </summary> public static void ThreadPool_Start() {WaitCallback w = new WaitCallback(AddA);ThreadPool.QueueUserWorkItem(w,"ThreadPool"); }11.4、Task類創(chuàng)建多線程
/// <summary> /// Task方式啟動 /// </summary> public static void Task_Start() {Action<object> add_Action = AddA;Task task = new Task(add_Action, "Task");task.Start(); }12、數(shù)組、鏈表、哈希、隊列、棧數(shù)據(jù)結(jié)構(gòu)特點(diǎn),各自優(yōu)點(diǎn)和缺點(diǎn)
數(shù)組(Array):
優(yōu)點(diǎn):查詢快,通過索引直接查找;有序添加,添加速度快,允許重復(fù);
缺點(diǎn):在中間部位添加、刪除比較復(fù)雜,大小固定,只能存儲一種類型的數(shù)據(jù);
如果應(yīng)用需要快速訪問數(shù)據(jù),很少插入和刪除元素,就應(yīng)該用數(shù)組。
鏈表(LinkedList):
優(yōu)點(diǎn):有序添加、增刪改速度快,對于鏈表數(shù)據(jù)結(jié)構(gòu),增加和刪除只要修改元素中的指針就可以了;
缺點(diǎn):查詢慢,如果要訪問鏈表中一個元素,就需要從第一個元素開始查找;
如果應(yīng)用需要經(jīng)常插入和刪除元素,就應(yīng)該用鏈表。
棧(Stack):
優(yōu)點(diǎn):提供后進(jìn)先出的存儲方式,添加速度快,允許重復(fù);
缺點(diǎn):只能在一頭操作數(shù)據(jù),存取其他項很慢;
隊列(Queue):
優(yōu)點(diǎn):提供先進(jìn)先出的存儲方式,添加速度快,允許重復(fù);
缺點(diǎn):只能在一頭添加,另一頭獲取,存取其他項很慢;
哈希(Hash):
特點(diǎn):散列表,不允許重復(fù);
優(yōu)點(diǎn):如果關(guān)鍵字已知則存取速度極快;
缺點(diǎn):如果不知道關(guān)鍵字則存取很慢,對存儲空間使用不充分;
13、應(yīng)用程序池集成模式和經(jīng)典模式的區(qū)別
- 如果托管應(yīng)用程序在采用集成模式的應(yīng)用程序池中知運(yùn)行,服務(wù)器將使用 IIS 和 ASP.NET 的集成請求處理管道來處理請求。
- 如果托管應(yīng)用程序在采用經(jīng)典模式的應(yīng)用程序池中運(yùn)行,服務(wù)器會繼續(xù)通過 Aspnet_isapi.dll
路由托管代碼請求,其處理請求的方式就像應(yīng)用程序在 IIS 6.0 中運(yùn)行一樣。
14、垃圾回收的基本原理
回收分為兩個階段: 標(biāo)記 –> 壓縮
標(biāo)記的過程,其實(shí)就是判斷對象是否可達(dá)的過程。當(dāng)所有的根都檢查完畢后,堆中將包含可達(dá)(已標(biāo)記)與不可達(dá)(未標(biāo)記)對象。
標(biāo)記完成后,進(jìn)入壓縮階段。在這個階段中,垃圾回收器線性的遍歷堆,以尋找不可達(dá)對象的連續(xù)內(nèi)存塊。并把可達(dá)對象移動到這里以壓縮堆。這個過程有點(diǎn)類似于磁盤空間的碎片整理。
如上圖所示,綠色框表示可達(dá)對象,黃色框?yàn)椴豢蛇_(dá)對象。不可達(dá)對象清除后,移動可達(dá)對象實(shí)現(xiàn)內(nèi)存壓縮(變得更緊湊)。
壓縮之后,“指向這些對象的指針”的變量和CPU寄存器現(xiàn)在都會失效,垃圾回收器必須重新訪問所有根,并修改它們來指向?qū)ο蟮男聝?nèi)存位置。這會造成顯著的性能損失。這個損失也是托管堆的主要缺點(diǎn)。
基于以上特點(diǎn),垃圾回收引發(fā)的回收算法也是一項研究課題。因?yàn)槿绻娴鹊酵泄芏褲M才開始執(zhí)行垃圾回收,那就真的太“慢”了。
垃圾回收算法 – 分代(Generation)算法
代是CLR垃圾回收器采用的一種機(jī)制,它唯一的目的就是提升應(yīng)用程序的性能。分代回收,速度顯然快于回收整個堆。
CLR托管堆支持3代:第0代,第1代,第2代。第0代的空間約為256KB,第1代約為2M,第2代約為10M。新構(gòu)造的對象會被分配到第0代。
如上圖所示,當(dāng)?shù)?代的空間滿時,垃圾回收器啟動回收,不可達(dá)對象(上圖C、E)會被回收,存活的對象被歸為第1代。
當(dāng)?shù)?代空間已滿,第1代也開始有很多不可達(dá)對象以至空間將滿時,這時兩代垃圾都將被回收。存活下來的對象(可達(dá)對象),第0代升為第1代,第1代升為第2代。
實(shí)際CLR的代回收機(jī)制更加“智能”,如果新創(chuàng)建的對象生存周期很短,第0代垃圾也會立刻被垃圾回收器回收(不用等空間分配滿)。另外,如果回收了第0代,發(fā)現(xiàn)還有很多對象“可達(dá)”,
并沒有釋放多少內(nèi)存,就會增大第0代的預(yù)算至512KB,回收效果就會轉(zhuǎn)變?yōu)?#xff1a;垃圾回收的次數(shù)將減少,但每次都會回收大量的內(nèi)存。如果還沒有釋放多少內(nèi)存,垃圾回收器將執(zhí)行
完全回收(3代),如果還是不夠,則會拋出“內(nèi)存溢出”異常。
也就是說,垃圾回收器會根據(jù)回收內(nèi)存的大小,動態(tài)的調(diào)整每一代的分配空間預(yù)算!達(dá)到自動優(yōu)化!
總結(jié)
垃圾回收背后有這樣一個基本的觀念:編程語言(大多數(shù)的)似乎總能訪問無限的內(nèi)存。而開發(fā)者可以一直分配、分配再分配——像魔法一樣,取之不盡用之不竭。
.NET垃圾回收器的基本工作原理是:通過最基本的標(biāo)記清除原理,清除不可達(dá)對象;再像磁盤碎片整理一樣壓縮、整理可用內(nèi)存;最后通過分代算法實(shí)現(xiàn)性能最優(yōu)化。
15、Redis集群搭建遇到了哪些問題?
1.IP回環(huán)地址的問題:
本地建立主從服務(wù)器的時候,bind 127.0.0.1是不會有問題;但是如果要建立集群,需要使用機(jī)子本身的IP地址。
2.卡在waiting for the cluster to join的問題:
首先檢查IP地址綁定是否正確,如果沒有問題,檢查集群配置文件是否重名:cluster-config-file 這個參數(shù)配置后面的文件名稱不能重復(fù)。另外appendfilename 這個參數(shù)名稱最好每個節(jié)點(diǎn)也不一樣
3.連接請求問題:
網(wǎng)上很多文檔說連接請求的命令是 redis-cli -c -p **** ,但是如果不是使用的本地回環(huán),這種鏈接是無法鏈接到集群的,必須在后面加IP地址:redis-cli -c -p **** -h?.***.***.
4.配置文件選項前面一定不能有空格
Not all 16384 slots are covered by nodes錯誤
在執(zhí)行redis-trib.rb create命令創(chuàng)建redis集群的時候,遇到了這個錯誤:Not all 16384 slots are covered by nodes.
總結(jié)
以上是生活随笔為你收集整理的Redis 分布式集群的几种方案及问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TK域名首次注册教程(咸干花生)
- 下一篇: linux cmake编译源码,linu