cf577 B. Modulo Sum - 鵲巢定理 + 01揹包
阿新 • • 發佈:2021-01-02
Redis
- Redis 是 C 語言開發的一個開源的(遵從 BSD 協議)高效能鍵值對(key-value)的記憶體資料庫,可以用作資料庫、快取、訊息中介軟體等。它是一種 NoSQL(not-only sql,泛指非關係型資料庫)的資料庫。
- Redis 的單執行緒模型:每一條到達redis服務端的命令不會立即執行,而是加入一個命令佇列,然後逐個執行,並且多個客戶端傳送的命令的執行順序是不確定的。但是可以確定的是不會有兩條命令被同時執行,不會產生併發問題。
- Redis 作為一個記憶體資料庫,為什麼能夠快速執行:
- 絕大部分請求是純粹的記憶體操作(非常快速)
- 採用單執行緒,避免了不必要的上下文切換和競爭條件
- 非阻塞IO - IO多路複用,Redis採用epoll做為I/O多路複用技術的實現,再加上Redis自身的事件處理模型將epoll中的連線,讀寫,關閉都轉換為了時間,不在I/O上浪費過多的時間。
- 五大資料型別:
- redis的持久化機制
- RDB:redis預設的持久化策略;把資料以快照的形式儲存在磁碟上。
- AOF:將每一個收到的寫命令都通過write函式追加到AOF檔案中。
- 單執行緒的redis為什麼這麼快
- 純記憶體操作
- 單執行緒操作,避免了頻繁的上下文切換
- 採用了非阻塞I/O多路複用機制
- 熱點資料和冷資料
- 熱點資料,快取才有價值。比如置頂的商品資訊,首頁資訊,這種設定了就要被讀取很多次的,可以考慮做快取。
- 冷資料,大部分資料可能還沒有再次訪問到就已經被擠出記憶體,不僅佔用記憶體,而且價值不大。
- 也存在修改頻率很高,但是又不得不考慮快取的場景。比如訪問量,收藏量,這類會多次訪問而又頻繁改變。此時可以將資料同步到redis,讀取的時候讀快取,改變的時候修改資料庫,然後同步快取。減少資料庫的讀取壓力。
- 過期策略以及記憶體淘汰機制
- Redis採用的是定期刪除+惰性刪除策略。缺點是定期刪除沒有抽查到,而且沒有獲取的key,將不會被清除,這樣redis的記憶體會越來越大。(引入記憶體淘汰機制)
- 為什麼不適用定時刪除?定時刪除,用一個定時器來負責監視key,過期則自動刪除。雖然記憶體及時釋放,但是十分消耗CPU資源。在大併發請求下,CPU要將時間應用在處理請求,而不是刪除key,因此沒有采用這一策略。
- 定期刪除:隨機抽查key,過期了就刪除
- 惰性刪除:每次獲取key的時候,判斷是否過期(如果設定了過期時間),過期了就刪掉。
- 記憶體淘汰機制
- volatile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰
- volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰
- volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰
- allkeys-lru:從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰
- allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰
- no-enviction(驅逐):禁止驅逐資料,新寫入操作會報錯
- ps:如果沒有設定expire(到期)的key, 不滿足先決條件(prerequisites);那麼 volatile-lru, volatile-random 和 volatile-ttl 策略的行為, 和 noeviction(不刪除) 基本上一致。
- 關於redis執行緒模型
- 多個套接字–>IO多路複用程式–>檔案事件分派器–>事件處理器
- 因為檔案事件分派器佇列的消費是單執行緒的,所以Redis才叫單執行緒模型。
- (原理)儘管多個檔案事件可能會併發地出現,但I/O多路複用程式總是會將所有產生事件的套接字都推到一個佇列裡面,然後通過這個佇列,以有序(sequentially)、同步(synchronously)、每次一個套接字的方式向檔案事件分派器傳送套接字:當上一個套接字產生的事件被處理完畢之後(該套接字為事件所關聯的事件處理器執行完畢), I/O多路複用程式才會繼續向檔案事件分派器傳送下一個套接字。
- 關於主從複製,讀寫分離
- Master 最好不要做任何持久化工作,如 RDB 記憶體快照和 AOF 日誌檔案
- 如果資料比較重要,某個 Slave 開啟 AOF 備份資料,策略設定為每秒同步一次
- 為了主從複製的速度和連線的穩定性, Master 和 Slave 最好在同一個區域網內
- 儘量避免在壓力很大的主庫上增加從庫
- 主從複製不要用圖狀結構,用單向連結串列結構更為穩定,即: Master <- Slave1 <- Slave2 <-Slave3
- redis實現分散式鎖
- Redis為單程序單執行緒模式,採用佇列模式將併發訪問變成序列訪問,且多客戶端對Redis的連線並不存在競爭關係Redis中可以使用SETNX命令實現分散式鎖。將 key 的值設為 value ,當且僅當 key 不存在。 若給定的 key 已經存在,則 SETNX 不做任何動作。使用完畢後刪除key即可解鎖。
- 解決死鎖:
- 通過Redis中expire()給鎖設定最大持有時間,如果超過,則Redis來幫我們釋放鎖。
- 使用 setnx key “當前系統時間+鎖持有的時間”和getset key “當前系統時間+鎖持有的時間”組合的命令就可以實現。
- 快取雪崩
- 分兩種情況:1.redis掛掉;2.redis快取大面積過期。
- 所有請求都會去請求資料庫,壓力瞬間增大,資料庫也會崩掉,然後導致整個服務崩掉
- 解決方法:
- 對於第1種情況:
- 事發前:實現Redis的高可用(主從架構+Sentinel(哨兵) 或者Redis Cluster(叢集)),儘量避免Redis掛掉這種情況發生。
- 事發中:萬一Redis真的掛了,我們可以設定本地快取(ehcache)+限流(hystrix),儘量避免我們的資料庫被幹掉(起碼能保證我們的服務還是能正常工作的)
- 事發後:redis持久化,重啟後自動從磁碟上載入資料,快速恢復快取資料。
- 對於第2種情況:
- 在快取的時候給過期時間加上一個隨機值,這樣就會大幅度的減少快取在同一時間過期。
- 對於第1種情況:
- 快取穿透
- 快取穿透是指查詢一個一定不存在的資料。由於快取不命中,並且出於容錯考慮,如果從資料庫查不到資料則不寫入快取,這將導致這個不存在的資料每次請求都要到資料庫去查詢,失去了快取的意義。黑客想把我的資料庫搞垮,每次請求的ID都是負數。這會導致我的快取就沒用了,請求全部都找資料庫去了,資料庫壓力也會瞬間增大。
- 解決方法:
- 由於請求的引數是不合法的(每次都請求不存在的引數),於是我們可以使用布隆過濾器(BloomFilter)或者壓縮filter提前攔截,不合法就不讓這個請求到資料庫層!
- 當我們從資料庫找不到的時候,我們也將這個空物件設定到快取裡邊去。下次再請求的時候,就可以從快取裡邊獲取了。這種情況我們一般會將空物件設定一個較短的過期時間。
Redis叢集搭建三種模式
- 以下三種模式均為叢集搭建方式,功能依次遞增
- 主從模式(master/slave)
- 特點
- 主資料庫可以進行讀寫操作,當讀寫操作導致資料變化時會自動將資料同步給從資料庫
- 從資料庫一般都是隻讀的,並且接收主資料庫同步過來的資料
- 一個master可以擁有多個slave,但是一個slave只能對應一個master
- slave掛了不影響其他slave的讀和master的讀和寫,重新啟動後會將資料從master同步過來
- master掛了以後,不影響slave的讀,但redis不再提供寫服務,master重啟後redis將重新對外提供寫服務
- master掛了以後,不會在slave節點中重新選一個master
- 缺點
- master節點在主從模式中唯一,若master掛掉,則redis無法對外提供寫服務。
- 特點
- 哨兵模式(Sentinel)
- 特點
- 主從模式的弊端就是不具備高可用性,當master掛掉以後,Redis將不能再對外提供寫入操作,因此sentinel應運而生,它的作用就是監控redis叢集的執行狀況
- sentinel模式是建立在主從模式的基礎上,如果只有一個Redis節點,sentinel就沒有任何意義
- 當master掛了以後,sentinel會在slave中選擇一個做為master,並修改它們的配置檔案,其他slave的配置檔案也會被修改,比如slaveof屬性會指向新的master
- 當master重新啟動後,它將不再是master而是做為slave接收新的master的同步資料
- sentinel因為也是一個程序有掛掉的可能,所以sentinel也會啟動多個形成一個sentinel叢集
- 多sentinel配置的時候,sentinel之間也會自動監控
- 當主從模式配置密碼時,sentinel也會同步將配置資訊修改到配置檔案中,不需要擔心
- 一個sentinel或sentinel叢集可以管理多個主從Redis,多個sentinel也可以監控同一個redis
- sentinel最好不要和Redis部署在同一臺機器,不然Redis的伺服器掛了以後,sentinel也掛了
- 工作機制
- 每個sentinel以每秒鐘一次的頻率向它所知的master,slave以及其他sentinel例項傳送一個 PING 命令
- 如果一個例項距離最後一次有效回覆 PING 命令的時間超過 down-after-milliseconds 選項所指定的值, 則這個例項會被sentinel標記為主觀下線。
- 如果一個master被標記為主觀下線,則正在監視這個master的所有sentinel要以每秒一次的頻率確認master的確進入了主觀下線狀態
- 當有足夠數量的sentinel(大於等於配置檔案指定的值)在指定的時間範圍內確認master的確進入了主觀下線狀態, 則master會被標記為客觀下線
- 在一般情況下, 每個sentinel會以每 10 秒一次的頻率向它已知的所有master,slave傳送 INFO 命令
- 當master被sentinel標記為客觀下線時,sentinel向下線的master的所有slave傳送 INFO 命令的頻率會從 10 秒一次改為 1 秒一次
- 若沒有足夠數量的sentinel同意master已經下線,master的客觀下線狀態就會被移除;若master重新向sentinel的 PING 命令返回有效回覆,master的主觀下線狀態就會被移除
- 特點
- 叢集模式(cluster)
- sentinel模式基本可以滿足一般生產的需求,具備高可用性。但是當資料量過大到一臺伺服器存放不下的情況時,主從模式或sentinel模式就不能滿足需求了,這個時候需要對儲存的資料進行分片,將資料儲存到多個Redis例項中。cluster模式的出現就是為了解決單機Redis容量有限的問題,將Redis的資料根據一定的規則分配到多臺機器。
- cluster可以說是sentinel和主從模式的結合體,通過cluster可以實現主從和master重選功能,所以如果配置兩個副本三個分片的話,就需要六個Redis例項。因為Redis的資料是根據一定規則分配到叢集的不同機器的,當資料量過大時,可以新增機器進行擴容。
- 主從模式(master/slave)