*一座哩、Redis封裝架構(gòu)講解
實(shí)際上NewLife.Redis是一個(gè)完整的Redis協(xié)議功能的實(shí)現(xiàn)苍凛,但是Redis的核心功能并沒(méi)有在這里面趣席,而是在NewLife.Core里面。
這里可以打開(kāi)看一下醇蝴,NewLife.Core里面有一個(gè)NewLife.Caching的命名空間宣肚,里面有一個(gè)Redis類(lèi),里面實(shí)現(xiàn)了Redis的基本功能悠栓;另一個(gè)類(lèi)是RedisClient是Redis的客戶(hù)端霉涨。
Redis的核心功能就是有這兩個(gè)類(lèi)實(shí)現(xiàn),RedisClient代表著Redis客戶(hù)端對(duì)服務(wù)器的一個(gè)連接惭适。Redis真正使用的時(shí)候有一個(gè)Redis連接池笙瑟,里面存放著很多個(gè)RedisClient對(duì)象。
所以我們Redis的封裝有兩層癞志,一層是NewLife.Core里面的Redis以及RedisClient往枷;另一層就是NewLife.Redis。這里面的FullRedis是對(duì)Redis的實(shí)現(xiàn)了Redis的所有的高級(jí)功能。
這里你也可以認(rèn)為NewLife.Redis是Redis的一個(gè)擴(kuò)展错洁。
二秉宿、Test實(shí)例講解Redis的基本使用
1、實(shí)例
打開(kāi)Program.cs看下代碼:
這里XTrace.UseConsole();是向控制臺(tái)輸出日志屯碴,方便調(diào)試使用查看結(jié)果描睦。
接下來(lái)看第一個(gè)例子Test1,具體的我都在代碼中進(jìn)行了注釋?zhuān)蠹铱梢钥聪拢?/p>
Set的時(shí)候窿锉,如果是字符串或者字符數(shù)據(jù)的話(huà)酌摇,Redis會(huì)直接保存起來(lái)(字符串內(nèi)部機(jī)制也是保存二進(jìn)制),如果是其他類(lèi)型嗡载,會(huì)默認(rèn)進(jìn)行json序列化然后再保存起來(lái)窑多。
Get的時(shí)候,如果是字符串或者字符數(shù)據(jù)會(huì)直接獲取洼滚,如果是其他類(lèi)型會(huì)進(jìn)行json反序列化埂息。
Set第三個(gè)參數(shù)過(guò)期時(shí)間單位是秒。
vs調(diào)試小技巧遥巴,按F5或者直接工具欄“啟動(dòng)”會(huì)編譯整個(gè)解決方案會(huì)很慢(VS默認(rèn))千康,可以選中項(xiàng)目然后右鍵菜單選擇調(diào)試->啟動(dòng)新實(shí)例,會(huì)只編譯將會(huì)用到的項(xiàng)目铲掐,這樣對(duì)調(diào)試來(lái)說(shuō)會(huì)快很多拾弃。
大家運(yùn)行調(diào)試后可以看到控制臺(tái)輸出的內(nèi)容:向右的箭頭=》是ic.Log=XTrace.Log輸出的日志。
字典的使用:對(duì)象的話(huà)摆霉,需要把json全部取出來(lái)豪椿,然后轉(zhuǎn)換成對(duì)象,而字典的話(huà)携栋,就可以直接取某個(gè)字段搭盾。
隊(duì)列是List結(jié)構(gòu)實(shí)現(xiàn)的,上游數(shù)據(jù)太多婉支,下游處理不過(guò)來(lái)的時(shí)候鸯隅,就可以使用這個(gè)隊(duì)列。上游的數(shù)據(jù)發(fā)到隊(duì)列向挖,然后下游慢慢的消費(fèi)蝌以。另一個(gè)應(yīng)用,跨語(yǔ)言的協(xié)同工作何之,比方說(shuō)其他語(yǔ)言實(shí)現(xiàn)的程序往隊(duì)列里面塞數(shù)據(jù)跟畅,然后另一種語(yǔ)言來(lái)進(jìn)行消費(fèi)處理。這種方式類(lèi)似MQ的概念帝美,雖然有點(diǎn)low,但是也很好用。
集合悼潭,用的比較多的是用在一個(gè)需要精確判斷的去重功能庇忌。像我們每天有三千萬(wàn)訂單,這三千萬(wàn)訂單可以有重復(fù)舰褪。這時(shí)候我想統(tǒng)計(jì)下一共有訂單皆疹,這時(shí)候直接數(shù)據(jù)庫(kù)group by是不大可能的,因?yàn)閿?shù)據(jù)庫(kù)中分了十幾張表占拍,這里分享個(gè)實(shí)戰(zhàn)經(jīng)驗(yàn):
比方說(shuō)攬收略就,商家發(fā)貨了,網(wǎng)點(diǎn)要把件收回來(lái)晃酒,但是收回來(lái)之前網(wǎng)點(diǎn)不知道自己有多少貨表牢,這時(shí)候我們做了一個(gè)功能,也就是訂單會(huì)發(fā)送到我們公司來(lái)贝次。我們會(huì)建一個(gè)time_site的key的集合崔兴,而且集合本身有去重的功能,而且我們可以很方便的通過(guò)set.Count功能來(lái)統(tǒng)計(jì)數(shù)量蛔翅,當(dāng)件被攬收以后敲茄,我們后臺(tái)把這個(gè)件從集合中Remove掉。然后這個(gè)Set中存在的就是網(wǎng)點(diǎn)還沒(méi)有攬收的件山析,這時(shí)候通過(guò)Count就會(huì)知道這個(gè)網(wǎng)點(diǎn)今天還有多少件沒(méi)有攬收堰燎。實(shí)際使用中這個(gè)數(shù)量比較大,因?yàn)橛袔兹f(wàn)個(gè)網(wǎng)點(diǎn)笋轨。
Redis中布隆過(guò)濾器秆剪,去重的,面試的時(shí)候問(wèn)的比較多翩腐。
小經(jīng)驗(yàn)分享:
數(shù)據(jù)庫(kù)中不合法的時(shí)間處理:判斷時(shí)間中的年份是否大于2000年鸟款,如果小于2000就認(rèn)為不合法;習(xí)慣大于小于號(hào)不習(xí)慣用等于號(hào)茂卦,這樣可以處理很多意外的數(shù)據(jù)何什;
Set的時(shí)候最好指定過(guò)期時(shí)間,防止有些需要?jiǎng)h除的數(shù)據(jù)我們忘記刪了等龙;
Redis異步盡量不用处渣,因?yàn)镽edis延遲本身很小,大概在100us-200us蛛砰,再一個(gè)就是Redis本身是單線(xiàn)程的罐栈,異步任務(wù)切換的耗時(shí)比網(wǎng)絡(luò)耗時(shí)還要大;
List用法:物聯(lián)網(wǎng)中數(shù)據(jù)上傳泥畅,量比較大時(shí)荠诬,我們可以把這些數(shù)據(jù)先放在Redis的List中,比如說(shuō)一秒鐘1萬(wàn)條,然后再批量取出來(lái)然后批量插入數(shù)據(jù)庫(kù)中柑贞。這時(shí)候要設(shè)置好key方椎,可以前綴+時(shí)間,對(duì)已處理的List可以進(jìn)行remove移除钧嘶。
2棠众、壓力測(cè)試
接下來(lái)看第四個(gè)例子,我們直接做壓力測(cè)試有决,代碼如下:
運(yùn)行的結(jié)果如下圖所示:
測(cè)試就是進(jìn)行g(shù)et,set remove,累加等的操作闸拿。大家可以看到在我本機(jī)上輕輕松松的到了六十萬(wàn),多線(xiàn)程的時(shí)候甚至到了一百多萬(wàn)书幕。
為什么會(huì)達(dá)到這么高的Ops呢新荤?下面給大家說(shuō)一下:
Bench會(huì)分根據(jù)線(xiàn)程數(shù)分多組進(jìn)行添刪改壓力測(cè)試;
rand參數(shù)按咒,是否隨機(jī)產(chǎn)生key/value迟隅;
batch批大小,分批執(zhí)行讀寫(xiě)操作励七,借助GetAll/SetAll進(jìn)行優(yōu)化智袭。
3、Redis中NB的函數(shù)來(lái)提升性能
上面的操作如果大家都掌握了就基本算Redis入門(mén)了掠抬,接下來(lái)進(jìn)行進(jìn)階吼野。如果能全然吃透,差不多就會(huì)比別人更勝一籌了两波。
GetAll()與SetAll()
GetAll:比方說(shuō)我要取十個(gè)key瞳步,這個(gè)時(shí)候可以用getall。這時(shí)候Redis就執(zhí)行了一次命令腰奋。比方說(shuō)我要取10個(gè)key那么用get的話(huà)要取10次单起,如果用getall的話(huà)要用1次。1次getall時(shí)間大概是get的一點(diǎn)幾倍劣坊,但是10次get的話(huà)就是10倍的時(shí)間嘀倒,這個(gè)賬你應(yīng)該會(huì)算吧?強(qiáng)烈推薦大家用getall局冰。
setall跟getall相似测蘑,批量設(shè)置K-V。
setall與getall性能很恐怖康二,官方公布的Ops也就10萬(wàn)左右碳胳,為什么我們的測(cè)試輕輕松松到五十萬(wàn)甚至上百萬(wàn)?因?yàn)槲覀兙陀昧藄etall,getall沫勿。如果get,set兩次以上挨约,建議用getall,setall味混。
Redis管道Pipeline
比如執(zhí)行10次命令會(huì)打包成一個(gè)包集體發(fā)過(guò)去執(zhí)行,這里實(shí)現(xiàn)的方式是StartPipeline()開(kāi)始诫惭,StopPipeline()結(jié)束中間的代碼就會(huì)以管道的形式執(zhí)行惜傲。
這里推薦使用更強(qiáng)的武器,AutoPipeline自動(dòng)管道屬性贝攒。管道操作到一定數(shù)量時(shí),自動(dòng)提交时甚,默認(rèn)0隘弊。使用了AutoPipeline,就不需要StartPipeline荒适,StopPipeline指定管道的開(kāi)始結(jié)束了梨熙。
Add與Replace
Add:Redis中沒(méi)有這個(gè)Key就添加,有了就不要添加刀诬,返回false咽扇;
Replace:有則替換,還會(huì)返回原來(lái)的值陕壹,沒(méi)有則不進(jìn)行操作质欲。
Add跟Replace就是實(shí)現(xiàn)Redis分布式鎖的關(guān)鍵。
三糠馆、Redis使用技巧嘶伟,經(jīng)驗(yàn)分享
在項(xiàng)目的Readme中,這里摘錄下:
1又碌、特性
在ZTO大數(shù)據(jù)實(shí)時(shí)計(jì)算廣泛應(yīng)用九昧,200多個(gè)Redis實(shí)例穩(wěn)定工作一年多,每天處理近1億包裹數(shù)據(jù)毕匀,日均調(diào)用量80億次铸鹰;
低延遲,Get/Set操作平均耗時(shí)200~600us(含往返網(wǎng)絡(luò)通信)皂岔;
大吞吐蹋笼,自帶連接池,最大支持1000并發(fā)凤薛;
高性能姓建,支持二進(jìn)制序列化(默認(rèn)用的json,json很低效缤苫,轉(zhuǎn)成二進(jìn)制性能會(huì)提升很多)速兔。
2、Redis經(jīng)驗(yàn)分享
在Linux上多實(shí)例部署活玲,實(shí)例個(gè)數(shù)等于處理器個(gè)數(shù)涣狗,各實(shí)例最大內(nèi)存直接為本機(jī)物理內(nèi)存谍婉,避免單個(gè)實(shí)例內(nèi)存撐爆(比方說(shuō)8核心處理器,那么就部署8個(gè)實(shí)例)镀钓。
把海量數(shù)據(jù)(10億+)根據(jù)key哈希(Crc16/Crc32)存放在多個(gè)實(shí)例上穗熬,讀寫(xiě)性能成倍增長(zhǎng)。
采用二進(jìn)制序列化丁溅,而非常見(jiàn)的Json序列化唤蔗。
合理設(shè)計(jì)每一對(duì)Key的Value大小,包括但不限于使用批量獲取窟赏,原則是讓每次網(wǎng)絡(luò)包控制在1.4k字節(jié)附近妓柜,減少通信次數(shù)(實(shí)際經(jīng)驗(yàn)幾十k,幾百k也是沒(méi)問(wèn)題的)涯穷。
Redis客戶(hù)端的Get/Set操作平均耗時(shí)200~600us(含往返網(wǎng)絡(luò)通信)棍掐,以此為參考評(píng)估網(wǎng)絡(luò)環(huán)境和Redis客戶(hù)端組件(達(dá)不到就看一下網(wǎng)絡(luò),序列化方式等等)拷况。
使用管道Pipeline合并一批命令作煌。
Redis的主要性能瓶頸是序列化、網(wǎng)絡(luò)帶寬和內(nèi)存大小赚瘦,濫用時(shí)處理器也會(huì)達(dá)到瓶頸粟誓。
在此我向大家推薦一個(gè)架構(gòu)學(xué)習(xí)交流圈:681065582 幫助突破J瓶頸 提升思維能力
其它可查優(yōu)化技巧。
以上經(jīng)驗(yàn)起意,源自于300多個(gè)實(shí)例4T以上空間一年多穩(wěn)定工作的經(jīng)驗(yàn)努酸,并按照重要程度排了先后順序,可根據(jù)場(chǎng)景需要酌情采用杜恰。
3获诈、緩存Redis的兄弟姐妹
Redis實(shí)現(xiàn)ICache接口,它的孿生兄弟MemoryCache心褐,內(nèi)存緩存舔涎,千萬(wàn)級(jí)吞吐率。
各應(yīng)用強(qiáng)烈建議使用ICache接口編碼設(shè)計(jì)逗爹,小數(shù)據(jù)時(shí)使用MemoryCache實(shí)現(xiàn)亡嫌;數(shù)據(jù)增大(10萬(wàn))以后,改用Redis實(shí)現(xiàn)掘而,不需要修改業(yè)務(wù)代碼挟冠。
四、關(guān)于一些疑問(wèn)的回復(fù)
這一Part我們會(huì)來(lái)聊聊大數(shù)據(jù)中Redis使用的經(jīng)驗(yàn):
Q1:一條數(shù)據(jù)多個(gè)key怎么設(shè)置比較合理袍睡?
A1:如果對(duì)性能要求不是很高直接用json序列化實(shí)體就好知染,沒(méi)必要使用字典進(jìn)行存儲(chǔ)。
Q2:隊(duì)列跟List有什么區(qū)別斑胜?左進(jìn)右出的話(huà)用List還是用隊(duì)列比較好控淡?
A2:隊(duì)列其實(shí)就是用List實(shí)現(xiàn)的嫌吠,也是基于List封裝的。左進(jìn)右出的話(huà)直接隊(duì)列就好掺炭。Redis的List結(jié)構(gòu)比較有意思辫诅,既可以左進(jìn)右出,也能右進(jìn)左出涧狮。所以它既可以實(shí)現(xiàn)列表結(jié)構(gòu)炕矮,也能隊(duì)列,還能實(shí)現(xiàn)棧者冤。
Q3:存放多個(gè)字段的類(lèi)性能一樣嗎吧享?
A3:大部分場(chǎng)景都不會(huì)有偏差,可能對(duì)于大公司數(shù)據(jù)量比較大的場(chǎng)景會(huì)有些偏差譬嚣。
Q4:大數(shù)據(jù)寫(xiě)入到數(shù)據(jù)庫(kù)之后,比如數(shù)據(jù)到億以上的時(shí)候钞它,統(tǒng)計(jì)分析拜银、查詢(xún)這塊,能不能分享些經(jīng)驗(yàn)遭垛。
A4:分表分庫(kù)尼桶,拆分到一千萬(wàn)以?xún)?nèi)。
Q5:CPU為何暴漲锯仪?
A5:程序員終極理念——CPU達(dá)到百分百泵督,然后性能達(dá)到最優(yōu),盡量不要浪費(fèi)庶喜。最痛恨的是——如果CPU不到百分百小腊,性能沒(méi)法提升了,說(shuō)明代碼有問(wèn)題久窟。
雖然Redis大家會(huì)用秩冈,但是我們可能平時(shí)不會(huì)有像這樣的大數(shù)據(jù)使用場(chǎng)景。希望本文能夠給大家一些值得借鑒的經(jīng)驗(yàn)斥扛。