基于內(nèi)存的單線程的可持久化存儲(chǔ)的NoSql數(shù)據(jù)庫(kù)
Redis為什么快!
1 Redis是C語(yǔ)言編寫锄贼,但是語(yǔ)言不是影響快慢的核心榴都,客戶端通過(guò)socket與服務(wù)端建立網(wǎng)絡(luò)通道,然后發(fā)送請(qǐng)求命令湖员。
2 相較其他基于磁盤的DB贫悄,Redis 的純內(nèi)存IO操作有著天然的性能優(yōu)勢(shì)
3 它是I/O多路復(fù)用,基于 epoll/select/kqueue 等 I/O 多路復(fù)用技術(shù)破衔,實(shí)現(xiàn)高吞吐的網(wǎng)絡(luò) I/O清女。
4 單線程無(wú)法利用多核,但是卻避免了多線程頻繁切換上下文和同步機(jī)制如鎖帶來(lái)的開銷晰筛。
5 請(qǐng)求大多數(shù)是IO密集型嫡丙,不是CPU密集型, 如果不考慮 RDB/AOF 等持久化方案读第,基于內(nèi)存的Redis是非呈锊快的,它的性能瓶頸在網(wǎng)絡(luò)IO怜瞒,也就是客戶端和服務(wù)端之間的網(wǎng)絡(luò)傳輸延遲父泳,因此 Redis 選擇了單線程的 I/O 多路復(fù)用來(lái)實(shí)現(xiàn)它的核心網(wǎng)絡(luò)模型般哼。
6 多線程調(diào)度過(guò)程中必然需要在 CPU 之間切換線程上下文 context,上下文的切換又涉及程序計(jì)數(shù)器惠窄、堆棧指針和程序狀態(tài)字等一系列的寄存器置換蒸眠、程序堆棧重置甚至是 CPU 高速緩存、TLB 快表的汰換杆融,如果是進(jìn)程內(nèi)的多線程切換還好一些楞卡,因?yàn)閱我贿M(jìn)程內(nèi)多線程共享進(jìn)程地址空間,線程上下文比之進(jìn)程上下文要小得多脾歇,如果是跨進(jìn)程調(diào)度蒋腮,則需要切換掉整個(gè)進(jìn)程地址空間。如果是單線程則可以規(guī)避進(jìn)程內(nèi)頻繁的線程切換開銷藕各,因?yàn)槌绦蚴冀K運(yùn)行在進(jìn)程中單個(gè)線程內(nèi)池摧,沒(méi)有多線程切換的場(chǎng)景。
7 如果 Redis 選擇多線程模型激况,又因?yàn)?Redis 是一個(gè)數(shù)據(jù)庫(kù)作彤,涉及到底層數(shù)據(jù)同步的問(wèn)題,必然會(huì)引入某些同步機(jī)制誉碴,比如鎖宦棺,而 Redis 不僅僅提供了簡(jiǎn)單的 key-value 數(shù)據(jù)結(jié)構(gòu),還有string, list黔帕、set,zset蹈丸,hash的數(shù)據(jù)結(jié)構(gòu)成黄,而不同的數(shù)據(jù)結(jié)構(gòu)對(duì)同步訪問(wèn)的加鎖粒度又不盡相同,可能會(huì)導(dǎo)致在操作數(shù)據(jù)過(guò)程中帶來(lái)很多加鎖解鎖的開銷逻杖,增加程序復(fù)雜度的同時(shí)還會(huì)降低性能奋岁。
windows配置密碼
- 1 在go的redis包中有個(gè)參數(shù) : TestOnBorrow 是一個(gè)測(cè)試鏈接可用性的方法.
- 2 win10里配置redis密碼,一勞永逸的辦法是改配置文件:redis.windows.conf 在requirepass處 后面將foobared改成要修改的密碼就行荸百。 啟動(dòng)的時(shí)候在CMD終端執(zhí)行啟動(dòng)服務(wù)命令:
-- 服務(wù)端啟動(dòng):redis-server redis.windows.conf (如果不行闻伶,就按tab鍵啟動(dòng)該行命令)
-- 客戶端啟動(dòng): redis-cli
-- 驗(yàn)證密碼: config get requirepass
-- 授權(quán)密碼 : auth **(123456)
-- 再次驗(yàn)證密碼: config get requirepass (最好用管理員權(quán)限操作)
Redis 重啟
- 1 redis-cli -h 127.0.0.1 -p 6379 shutdown
- 2 執(zhí)行后如果報(bào)權(quán)限錯(cuò)誤:NOAUTH Authentication required 就執(zhí)行輸入密碼: auth 123456(密碼)
- 3 再shutdown
- 4 再啟動(dòng)服務(wù): redis-server redis.windows.conf
redis對(duì)鍵的刪除機(jī)制
- 被動(dòng)刪除:當(dāng)讀/寫一個(gè)已經(jīng)過(guò)期的key時(shí),會(huì)觸發(fā)惰性刪除策略够话,直接刪除掉這個(gè)過(guò)期key
- 主動(dòng)刪除:由于惰性刪除策略無(wú)法保證冷數(shù)據(jù)被及時(shí)刪掉蓝翰,所以Redis會(huì)定期主動(dòng)淘汰一批已過(guò)期的key
- 當(dāng)前已用內(nèi)存超過(guò)maxmemory限定時(shí),觸發(fā)主動(dòng)清理策略
-- 被動(dòng)刪除是當(dāng)女嘲,這個(gè)key被操作時(shí)畜份,redis才會(huì)被動(dòng)檢查該key是否過(guò)期,過(guò)期就刪除并且返回NIL。這種刪除策略對(duì)CPU是友好欣尼,只在操作時(shí)才會(huì)被刪除爆雹,但是有一些key永遠(yuǎn)不會(huì)訪問(wèn)到,服務(wù)器也不會(huì)主動(dòng)去刪除,對(duì)于依賴于內(nèi)存的Redis服務(wù)器來(lái)說(shuō)钙态,無(wú)用的垃圾數(shù)據(jù)會(huì)占用大量?jī)?nèi)存空間.
-- 主動(dòng)策略慧起,定期會(huì)檢查自身的資源和狀態(tài),以及整理册倒,讓服務(wù)器保持在一個(gè)健康的穩(wěn)定狀態(tài)蚓挤,叫常規(guī)操作.這種刪除彌補(bǔ)了被動(dòng)刪除上對(duì)內(nèi)存的不友好.
-- 實(shí)際上Redis不會(huì)準(zhǔn)確將整個(gè)數(shù)據(jù)庫(kù)中最久未被使用的鍵刪除,而是隨機(jī)刪除
redis緩存:穿透剩失,雪崩屈尼,擊穿,的解決思路
1 緩存穿透是指查詢一個(gè)不存在的數(shù)據(jù)拴孤。例如:從緩存redis沒(méi)有命中脾歧,需要從mysql數(shù)據(jù)庫(kù)查詢,查不到數(shù)據(jù)則不寫入緩存演熟,這將導(dǎo)致這個(gè)不存在的數(shù)據(jù)每次請(qǐng)求都要到數(shù)據(jù)庫(kù)去查詢鞭执,造成緩存穿透
2 如果查詢r(jià)edis沒(méi)有,然后再去Mysql查芒粹,還是沒(méi)有的話就寫一個(gè)默認(rèn)值Null到redis里兄纺,然后把過(guò)期時(shí)間expire設(shè)置成30s秒(設(shè)置太長(zhǎng)會(huì)導(dǎo)致正常情況也沒(méi)法使用)。這樣可以防止攻擊用戶反復(fù)用同一個(gè)id暴力攻擊化漆,第二次到緩沖中獲取就有值了估脆,而不會(huì)繼續(xù)訪問(wèn)數(shù)據(jù)庫(kù)。設(shè)置一個(gè)過(guò)期時(shí)間或者當(dāng)有值的時(shí)候?qū)⒕彺嬷械闹堤鎿Q掉即可座云。接口層增加校驗(yàn)疙赠,如用戶鑒權(quán)校驗(yàn),id做基礎(chǔ)校驗(yàn)朦拖,id<=0的直接攔截圃阳。
3 可以給key設(shè)置一些格式規(guī)則,然后查詢之前先過(guò)濾掉不符合規(guī)則的Key璧帝。
4 穿透的解決方案:1裝一個(gè)布隆過(guò)濾器捍岳,訪問(wèn)Redis之前,在布隆過(guò)濾器里查詢這個(gè)Key是否存在睬隶,存在再去訪問(wèn)Redis锣夹,不存在就直接返回出去1 個(gè)默認(rèn)的空對(duì)象過(guò)去,比如:"null" 理疙,再設(shè)置一個(gè)過(guò)期或當(dāng)有值的時(shí)候?qū)⒕彺娴闹堤鎿Q即可晕城,第二次訪問(wèn)的時(shí)候就有值了。
5 雪崩的解決思路:1在原有的失效時(shí)間上加上一個(gè)隨機(jī)值窖贤,比如1-5分鐘隨機(jī)砖顷。這樣就避免了因?yàn)椴捎孟嗤倪^(guò)期時(shí)間導(dǎo)致的緩存雪崩贰锁。2 使用熔斷機(jī)制。當(dāng)流量到達(dá)一定的閾值時(shí)滤蝠,就直接返回“系統(tǒng)擁擠”之類的提示豌熄,防止過(guò)多的請(qǐng)求打在數(shù)據(jù)庫(kù)上。至少能保證一部分用戶是可以正常使用物咳,其他用戶多刷新幾次也能得到結(jié)果锣险。 3 提高數(shù)據(jù)庫(kù)的容災(zāi)能力,可以使用分庫(kù)分表览闰,讀寫分離的策略芯肤。 4 為了防止Redis宕機(jī)導(dǎo)致緩存雪崩的問(wèn)題,可以搭建Redis集群,提高Redis的容災(zāi)性。
6 緩存擊穿解決思路: 1如果業(yè)務(wù)允許的話队萤,對(duì)于熱點(diǎn)的key可以設(shè)置永不過(guò)期的key 2 使用互斥鎖。如果緩存失效的情況击蹲,只有拿到鎖才可以查詢數(shù)據(jù)庫(kù),降低了在同一時(shí)刻打在數(shù)據(jù)庫(kù)上的請(qǐng)求婉宰,防止數(shù)據(jù)庫(kù)打死歌豺。當(dāng)然這樣會(huì)導(dǎo)致系統(tǒng)的性能變差。
可持久化存儲(chǔ)的2種方式:
RDB持久化方式是默認(rèn)開啟的心包,在配置文件寫Yes类咧,AOF是默認(rèn)關(guān)閉的,需手動(dòng)開啟蟹腾。
1 RDB持久化是指在指定的時(shí)間間隔內(nèi)將內(nèi)存中的數(shù)據(jù)集快照寫入磁盤轮听,實(shí)際操作過(guò)程是fork一個(gè)子進(jìn)程,先將數(shù)據(jù)集寫入臨時(shí)文件岭佳,寫入成功后,再替換之前的文件萧锉,用二進(jìn)制壓縮存儲(chǔ)
2 AOF持久化以日志的形式記錄服務(wù)器所處理的每一個(gè)寫珊随、刪除操作,查詢操作不會(huì)記錄柿隙,以文本的方式記錄叶洞,可以打開文件看到詳細(xì)的操作記錄。
3 RDB的優(yōu)點(diǎn):1一旦系統(tǒng)出現(xiàn)災(zāi)難性故障禀崖,我們可以非常容易的進(jìn)行恢復(fù) 2 對(duì)于災(zāi)難恢復(fù)而言衩辟,RDB是非常不錯(cuò)的選擇。因?yàn)槲覀兛梢苑浅]p松的將一個(gè)單獨(dú)的文件壓縮后再轉(zhuǎn)移到其它存儲(chǔ)介質(zhì)上 3只要fork出子進(jìn)程波附,之后再由子進(jìn)程完成這些持久化的工作艺晴,這樣就可以極大的避免服務(wù)進(jìn)程執(zhí)行IO操作了 4相比于AOF機(jī)制昼钻,如果數(shù)據(jù)集很大,RDB的啟動(dòng)效率會(huì)更高
4 AOF持久化存儲(chǔ)封寞,就算系統(tǒng)宕機(jī)了然评,下次重啟前還是能通過(guò)redis-check-aof工具來(lái)幫助我們解決數(shù)據(jù)一致性的問(wèn)題。AOF包含一個(gè)格式清晰狈究、易于理解的日志文件用于記錄所有的修改操作碗淌。事實(shí)上,我們也可以通過(guò)該文件完成數(shù)據(jù)的重建抖锥。RDB 在恢復(fù)大數(shù)據(jù)集時(shí)的速度比 AOF 的恢復(fù)速度要快
Redis在win10下利用AOF做可持久化存儲(chǔ)
- 1 先把redis.windows.conf 配置文件里的appendonly no 改成:appendonly yes
- 2 配置好后就重啟Redis亿眠,然后會(huì)發(fā)現(xiàn)多了個(gè)文件:appendonly.aof
-
3 輸入命令,插入數(shù)據(jù):
image.png - 4 flushall 最后輸入這個(gè)清除剛剛插入的數(shù)據(jù)
- 5 再keys *就沒(méi)有數(shù)據(jù)了
-
6 打開appendonly.aof文件磅废,可以看到:
image.png -
7 去掉最后面的flushall(也可以按照redis協(xié)議增加命令)纳像,重啟客戶端和服務(wù)端,看數(shù)據(jù)是否真的持久化了:
image.png - 8 到這一步就已經(jīng)證明利用AOF形式進(jìn)行可持久化存儲(chǔ)成功还蹲!
Redis的優(yōu)化
1 隨著并發(fā)的流量越來(lái)越大爹耗,單線程IO的瓶頸已經(jīng)越來(lái)越明顯,優(yōu)化的方向有2個(gè)
-- 優(yōu)化網(wǎng)絡(luò)IO模塊
-- 提高機(jī)器內(nèi)存讀寫的速度
網(wǎng)絡(luò) I/O 的優(yōu)化又可以分為兩個(gè)方向:
-- 1 零拷貝技術(shù)或者 DPDK 技術(shù)
-- 2 利用多核2 零拷貝技術(shù)有其局限性谜喊,無(wú)法完全適配 Redis 這一類復(fù)雜的網(wǎng)絡(luò) I/O 場(chǎng)景潭兽。利用多核優(yōu)勢(shì)就成了優(yōu)化網(wǎng)絡(luò) I/O 性價(jià)比最高的方案。Redis在6.0版本之后引入了多線程斗遏,多線程跟單線程模型是一樣的山卦,區(qū)別在于變動(dòng)的地方只是把讀取客戶端請(qǐng)求命令和回寫響應(yīng)數(shù)據(jù)的邏輯異步化了,交給 I/O 線程去完成诵次。I/O 線程僅僅是讀取和解析客戶端命令而不會(huì)真正去執(zhí)行命令账蓉,客戶端命令的執(zhí)行最終還是要在主線程上完成。
3 Redis 的多線程模型是全程無(wú)鎖逾一,這是通過(guò)原子操作+交錯(cuò)訪問(wèn)來(lái)實(shí)現(xiàn)的铸本,主線程和 I/O 線程之間共享的變量有三個(gè):io_threads_pending計(jì)數(shù)器、io_threads_op I/O 標(biāo)識(shí)符和 io_threads_list 線程本地任務(wù)隊(duì)列遵堵。3個(gè)變量的訪問(wèn)是相互錯(cuò)開的箱玷,不會(huì)引發(fā)數(shù)據(jù)競(jìng)爭(zhēng)的問(wèn)題。io_threads_pending 是原子變量陌宿,不需要加鎖保護(hù)(1:利用原子操作+交錯(cuò)訪問(wèn)實(shí)現(xiàn)無(wú)鎖的多線程模型锡足。 2:以及通過(guò)設(shè)置 CPU 親和性,隔離主進(jìn)程和其他子進(jìn)程壳坪,讓多線程網(wǎng)絡(luò)模型能發(fā)揮最大的性能)
如何解決DB和緩存一致性問(wèn)題舶得?
1 讀的時(shí)候先從Redis讀取,如果Redis中沒(méi)有爽蝴,那么再去數(shù)據(jù)庫(kù)中讀沐批,讀完之后放回Redis纫骑,然后返回響應(yīng)。寫的時(shí)候先刪除緩存珠插,然后再去寫入數(shù)據(jù)庫(kù)惧磺,然后等待100毫秒再次刪除緩存。這里有幾點(diǎn)點(diǎn)需要說(shuō)明: 為什么是先刪除緩存而不是先更新數(shù)據(jù)捻撑? 假設(shè)我們先更新數(shù)據(jù)磨隘,再刪除緩存,那么存在數(shù)據(jù)更新成功顾患,但是刪除緩存失敗的情況番捂。先刪除緩存,如果數(shù)據(jù)庫(kù)寫操作成功江解,下次讀緩存時(shí)會(huì)從數(shù)據(jù)庫(kù)獲取新的數(shù)據(jù)并放入緩存设预;如果數(shù)據(jù)庫(kù)寫操作失敗,那么再次讀到的數(shù)據(jù)也是舊的數(shù)據(jù)犁河,也保證了緩存的一致性鳖枕。另外,有的緩存數(shù)據(jù)是經(jīng)過(guò)計(jì)算的數(shù)據(jù)桨螺,不是單純的某個(gè)字段的值宾符,如果去更新的話會(huì)是一個(gè)復(fù)雜的過(guò)程,倒不如下次獲取的時(shí)候去計(jì)算并放入緩存灭翔,這也是一個(gè)“懶”的思想魏烫。 為什么寫入數(shù)據(jù)庫(kù)后還要再次刪除緩存?采用延時(shí)雙刪策略 我們?cè)跀?shù)據(jù)庫(kù)寫操作的時(shí)候是不能保證沒(méi)有讀的操作,特別是在高并發(fā)場(chǎng)景下肝箱,往往數(shù)據(jù)庫(kù)寫操作還沒(méi)完成哄褒,就已經(jīng)有讀的操作完成并將修改前的數(shù)據(jù)放入緩存,這也就造成了緩存和數(shù)據(jù)庫(kù)中的數(shù)據(jù)不一致的問(wèn)題煌张。那么解決這個(gè)問(wèn)題有一下方法
2 數(shù)據(jù)庫(kù)寫操作完成后再次刪除緩存呐赡,這樣出現(xiàn)不一致的時(shí)間就只會(huì)在數(shù)據(jù)庫(kù)寫操作和再次刪除緩存這段時(shí)間內(nèi),如果業(yè)務(wù)能夠容忍短時(shí)間的不一致骏融,可以采用這個(gè)方法罚舱。
3 如果業(yè)務(wù)對(duì)一致性要求較高,那么可以在第一次刪除緩存后對(duì)后續(xù)的操作做串行處理绎谦,后續(xù)的所有操作都需要等待數(shù)據(jù)庫(kù)寫操作的完成。具體的代碼思路可以是這樣子粥脚,將請(qǐng)求放入JVM的一個(gè)Queue中窃肠,將請(qǐng)求積壓在隊(duì)列中,同步等待寫操作的完成刷允。這個(gè)思路的實(shí)現(xiàn)有些復(fù)雜冤留,還涉及到如果Queue中積累的請(qǐng)求過(guò)多該怎么處理碧囊,請(qǐng)求過(guò)多的話也就說(shuō)明這是個(gè)熱點(diǎn)key。
4 第二種方案:異步更新緩存(基于訂閱binlog的同步機(jī)制) 技術(shù)整體思路: MySQL binlog增量訂閱消費(fèi)+消息隊(duì)列+增量數(shù)據(jù)更新到redis 讀Redis:熱數(shù)據(jù)基本都在Redis 寫MySQL:增刪改都是操作MySQL 更新Redis數(shù)據(jù):MySQ的數(shù)據(jù)操作binlog纤怒,來(lái)更新到Redis Redis更新 1)數(shù)據(jù)操作主要分為兩大塊: 一個(gè)是全量(將全部數(shù)據(jù)一次寫入到redis) 一個(gè)是增量(實(shí)時(shí)更新) 這里說(shuō)的是增量,指的是mysql的update糯而、insert、delate變更數(shù)據(jù)泊窘。 2)讀取binlog后分析 熄驼,利用消息隊(duì)列,推送更新各臺(tái)的redis緩存數(shù)據(jù)。 這樣一旦MySQL中產(chǎn)生了新的寫入烘豹、更新瓜贾、刪除等操作,就可以把binlog相關(guān)的消息推送至Redis携悯,Redis再根據(jù)binlog中的記錄祭芦,對(duì)Redis進(jìn)行更新。 其實(shí)這種機(jī)制憔鬼,很類似MySQL的主從備份機(jī)制龟劲,因?yàn)镸ySQL的主備也是通過(guò)binlog來(lái)實(shí)現(xiàn)的數(shù)據(jù)一致性。 這里可以結(jié)合使用canal(阿里的一款開源框架)轴或,通過(guò)該框架可以對(duì)MySQL的binlog進(jìn)行訂閱昌跌,而canal正是模仿了mysql的slave數(shù)據(jù)庫(kù)的備份請(qǐng)求,使得Redis的數(shù)據(jù)更新達(dá)到了相同的效果侮叮。 當(dāng)然避矢,這里的消息推送工具你也可以采用別的第三方:kafka、rabbitMQ等來(lái)實(shí)現(xiàn)推送更新Redis囊榜。 以上就是Redis和MySQL數(shù)據(jù)一致性詳解