[{"data":1,"prerenderedAt":1291},["ShallowReactive",2],{"article-\u002Farticles\u002Fredis-data-structures-use-cases":3},{"id":4,"title":5,"body":6,"date":1279,"description":628,"excerpt":1280,"extension":1281,"meta":1282,"navigation":720,"path":1283,"seo":1284,"stem":1285,"tags":1286,"__hash__":1290},"articles\u002Farticles\u002Fredis-data-structures-use-cases.md","Redis 不只是快取：五種資料結構、各自的使用情境與上線前要知道的事",{"type":7,"value":8,"toc":1260},"minimark",[9,14,23,48,60,67,70,77,82,85,153,159,199,203,206,258,262,283,290,294,297,349,353,382,386,389,441,445,478,482,489,544,548,586,589,615,619,622,995,1006,1010,1013,1027,1030,1040,1122,1131,1134,1137,1158,1161,1164,1246,1249,1256],[10,11,13],"h2",{"id":12},"redis-是什麼","Redis 是什麼",[15,16,17,18,22],"p",{},"Redis 是一個",[19,20,21],"strong",{},"把資料放在記憶體裡","的 Key-Value 資料庫。它快的原因有三個：",[24,25,26,33,42],"ul",{},[27,28,29,32],"li",{},[19,30,31],{},"資料在記憶體","：讀寫不用碰磁碟，單一指令通常在微秒等級完成",[27,34,35,38,39],{},[19,36,37],{},"單執行緒執行指令","：所有指令依序一個一個執行，沒有鎖的競爭。Redis 6.0 之後網路 I\u002FO 可以多執行緒處理，但",[19,40,41],{},"執行指令的仍然是單一執行緒",[27,43,44,47],{},[19,45,46],{},"資料結構內建在伺服器端","：要對一個集合加一個元素，不用「讀出整個集合 → 改 → 寫回」，直接下一個指令就好",[15,49,50,51,54,55,59],{},"第二點帶來一個非常重要的特性：",[19,52,53],{},"每一個指令都是原子的","。",[56,57,58],"code",{},"INCR"," 一個計數器，就算一千個請求同時打進來，也不會有兩個請求讀到同一個值、互相覆蓋。這是很多應用情境的基礎。",[15,61,62,63,66],{},"但它也代表另一件事：",[19,64,65],{},"一個很慢的指令，會卡住所有其他的指令","。後面的注意事項大多都和這點有關。",[10,68,69],{"id":69},"五種主要的資料結構",[15,71,72],{},[73,74],"img",{"alt":75,"src":76},"Redis 五種資料結構示意圖：String 是單一值，例如 house:1024:views 為 3582，用 INCR 計數；Hash 是欄位與值的集合，例如 cart:u77 內有物件與數量；List 是有序串列，例如 recent:u77 用 LPUSH 加 LTRIM 保留最近 10 筆瀏覽；Set 是不重複集合，例如 fav:u77 收藏的物件，可用 SINTER 取交集；Sorted Set 是依分數排序的集合，例如 hot:houses:2023-02-21 以瀏覽數為分數，ZREVRANGE 取前 10 名做排行榜","\u002Fimages\u002Farticles\u002Fredis-data-structures.svg",[78,79,81],"h3",{"id":80},"string最基本的值","String：最基本的值",[15,83,84],{},"一個 Key 對應一個值，值可以是字串、數字或序列化後的 JSON，最大 512MB（但實務上不該放這麼大）。",[86,87,88,101],"table",{},[89,90,91],"thead",{},[92,93,94,98],"tr",{},[95,96,97],"th",{},"指令",[95,99,100],{},"用途",[102,103,104,115,125,139],"tbody",{},[92,105,106,112],{},[107,108,109],"td",{},[56,110,111],{},"SET key value EX 300",[107,113,114],{},"寫入並設定 300 秒後過期",[92,116,117,122],{},[107,118,119],{},[56,120,121],{},"GET key",[107,123,124],{},"讀取",[92,126,127,136],{},[107,128,129,132,133],{},[56,130,131],{},"INCR key","／",[56,134,135],{},"INCRBY key 5",[107,137,138],{},"原子地加一、加五",[92,140,141,146],{},[107,142,143],{},[56,144,145],{},"SET key value NX",[107,147,148,149,152],{},"只有 Key ",[19,150,151],{},"不存在","時才寫入",[15,154,155,158],{},[19,156,157],{},"情境","：",[24,160,161,171,181,190],{},[27,162,163,166,167,170],{},[19,164,165],{},"物件瀏覽次數","：每次瀏覽 ",[56,168,169],{},"INCR house:1024:views","，不用每次都更新資料庫，定時再把數字寫回去",[27,172,173,176,177,180],{},[19,174,175],{},"快取","：把查詢結果序列化後 ",[56,178,179],{},"SET","，設定過期時間",[27,182,183,158,186,189],{},[19,184,185],{},"限流",[56,187,188],{},"INCR rate:{ip}:{分鐘}","，第一次時設定 60 秒過期，數字超過上限就拒絕",[27,191,192,158,195,198],{},[19,193,194],{},"防止重複送出",[56,196,197],{},"SET submit:{訂單號} 1 NX EX 10","，回傳失敗就代表 10 秒內已經送過了",[78,200,202],{"id":201},"hash一個物件的多個欄位","Hash：一個物件的多個欄位",[15,204,205],{},"一個 Key 底下有多個「欄位 → 值」，很像一個小型的物件或字典。",[86,207,208,216],{},[89,209,210],{},[92,211,212,214],{},[95,213,97],{},[95,215,100],{},[102,217,218,228,238,248],{},[92,219,220,225],{},[107,221,222],{},[56,223,224],{},"HSET key field value",[107,226,227],{},"設定一個欄位",[92,229,230,235],{},[107,231,232],{},[56,233,234],{},"HGET key field",[107,236,237],{},"讀一個欄位",[92,239,240,245],{},[107,241,242],{},[56,243,244],{},"HINCRBY key field 1",[107,246,247],{},"對一個欄位原子地加減",[92,249,250,255],{},[107,251,252],{},[56,253,254],{},"HGETALL key",[107,256,257],{},"讀出全部欄位",[15,259,260,158],{},[19,261,157],{},[24,263,264,277],{},[27,265,266,158,269,272,273,276],{},[19,267,268],{},"購物車",[56,270,271],{},"cart:{使用者}"," 底下，欄位是商品 ID、值是數量。加一件商品就是 ",[56,274,275],{},"HINCRBY cart:u77 sku-1 1","，不用把整台購物車讀出來改",[27,278,279,282],{},[19,280,281],{},"物件的部分欄位常常變動","：例如物件的瀏覽數、收藏數、狀態各自是一個欄位，各自更新，不必每次都重新序列化整個物件",[15,284,285,286,289],{},"和「把整個物件序列化成 JSON 存進 String」相比，Hash 的優勢是",[19,287,288],{},"可以只讀、只改其中一個欄位","；缺點是巢狀結構不好表達，而且不能對單一欄位設定過期時間。",[78,291,293],{"id":292},"list有順序的串列","List：有順序的串列",[15,295,296],{},"可以從頭或尾加入、取出的串列。",[86,298,299,307],{},[89,300,301],{},[92,302,303,305],{},[95,304,97],{},[95,306,100],{},[102,308,309,319,329,339],{},[92,310,311,316],{},[107,312,313],{},[56,314,315],{},"LPUSH key value",[107,317,318],{},"從左邊（頭）加入",[92,320,321,326],{},[107,322,323],{},[56,324,325],{},"LTRIM key 0 9",[107,327,328],{},"只保留第 0～9 筆",[92,330,331,336],{},[107,332,333],{},[56,334,335],{},"LRANGE key 0 9",[107,337,338],{},"讀取第 0～9 筆",[92,340,341,346],{},[107,342,343],{},[56,344,345],{},"BRPOP key timeout",[107,347,348],{},"從右邊取出，沒有資料就等待",[15,350,351,158],{},[19,352,157],{},[24,354,355,368],{},[27,356,357,166,360,363,364,367],{},[19,358,359],{},"最近瀏覽的 10 個物件",[56,361,362],{},"LPUSH recent:u77 1024"," 再 ",[56,365,366],{},"LTRIM recent:u77 0 9","，串列永遠只有最新的 10 筆",[27,369,370,373,374,377,378,381],{},[19,371,372],{},"簡單的工作佇列","：生產者 ",[56,375,376],{},"LPUSH","、消費者 ",[56,379,380],{},"BRPOP","。但取出後處理到一半當掉，這筆工作就沒了，也沒有重試、死信這些機制，重要的工作還是交給真正的訊息佇列",[78,383,385],{"id":384},"set不重複的集合","Set：不重複的集合",[15,387,388],{},"元素不重複、沒有順序的集合，而且可以做交集、聯集、差集。",[86,390,391,399],{},[89,392,393],{},[92,394,395,397],{},[95,396,97],{},[95,398,100],{},[102,400,401,411,421,431],{},[92,402,403,408],{},[107,404,405],{},[56,406,407],{},"SADD key member",[107,409,410],{},"加入（已存在就忽略）",[92,412,413,418],{},[107,414,415],{},[56,416,417],{},"SISMEMBER key member",[107,419,420],{},"判斷是否存在",[92,422,423,428],{},[107,424,425],{},[56,426,427],{},"SINTER key1 key2",[107,429,430],{},"交集",[92,432,433,438],{},[107,434,435],{},[56,436,437],{},"SCARD key",[107,439,440],{},"元素數量",[15,442,443,158],{},[19,444,157],{},[24,446,447,460,469],{},[27,448,449,158,452,455,456,459],{},[19,450,451],{},"收藏清單",[56,453,454],{},"SADD fav:u77 1024","，重複按收藏也不會出現兩筆；",[56,457,458],{},"SISMEMBER"," 判斷這個物件要不要顯示「已收藏」",[27,461,462,158,465,468],{},[19,463,464],{},"共同關注",[56,466,467],{},"SINTER fav:u77 fav:u88","，兩個人都收藏的物件",[27,470,471,158,474,477],{},[19,472,473],{},"標籤",[56,475,476],{},"tag:近捷運"," 底下放所有符合的物件 ID，多個條件就取交集",[78,479,481],{"id":480},"sorted-set依分數排序的集合","Sorted Set：依分數排序的集合",[15,483,484,485,488],{},"每個元素多了一個",[19,486,487],{},"分數（Score）","，Redis 會一直依分數維持排序。這是 Redis 最有特色的結構。",[86,490,491,499],{},[89,492,493],{},[92,494,495,497],{},[95,496,97],{},[95,498,100],{},[102,500,501,511,521,534],{},[92,502,503,508],{},[107,504,505],{},[56,506,507],{},"ZINCRBY key 1 member",[107,509,510],{},"把某個元素的分數加一",[92,512,513,518],{},[107,514,515],{},[56,516,517],{},"ZREVRANGE key 0 9 WITHSCORES",[107,519,520],{},"分數由高到低取前 10 名",[92,522,523,531],{},[107,524,525,132,528],{},[56,526,527],{},"ZRANK",[56,529,530],{},"ZREVRANK key member",[107,532,533],{},"某個元素的名次",[92,535,536,541],{},[107,537,538],{},[56,539,540],{},"ZRANGEBYSCORE key min max",[107,542,543],{},"取分數在某個範圍內的元素",[15,545,546,158],{},[19,547,157],{},[24,549,550,570,580],{},[27,551,552,166,555,558,559,562,563,566,567],{},[19,553,554],{},"熱門排行榜",[56,556,557],{},"ZINCRBY hot:houses:2023-02-21 1 1024","，首頁直接 ",[56,560,561],{},"ZREVRANGE"," 取前 10 名，不用每次對資料庫做 ",[56,564,565],{},"GROUP BY"," 加 ",[56,568,569],{},"ORDER BY",[27,571,572,575,576,579],{},[19,573,574],{},"延遲任務","：分數放「要執行的時間戳」，排程每秒 ",[56,577,578],{},"ZRANGEBYSCORE key 0 {現在}"," 取出到期的任務",[27,581,582,585],{},[19,583,584],{},"滑動視窗限流","：分數放請求的時間戳，每次先刪掉一分鐘以前的，再數剩下幾筆",[78,587,588],{"id":588},"其他結構",[24,590,591,597,603,609],{},[27,592,593,596],{},[19,594,595],{},"Bitmap","：用一個 bit 代表一個狀態，例如每日簽到，一個使用者一年只要 365 個 bit",[27,598,599,602],{},[19,600,601],{},"HyperLogLog","：用約 12KB 估算「不重複的數量」，例如每日不重複訪客數，有約 0.81% 的誤差",[27,604,605,608],{},[19,606,607],{},"Geo","：存經緯度，查詢某個點方圓幾公里內的成員",[27,610,611,614],{},[19,612,613],{},"Stream","：5.0 加入的訊息串流，支援 Consumer Group 與 Ack，比 List 更適合做訊息佇列",[10,616,618],{"id":617},"使用情境房仲網站的物件頁","使用情境：房仲網站的物件頁",[15,620,621],{},"一個物件頁面打開時，背後可以這樣用 Redis：",[623,624,629],"pre",{"className":625,"code":626,"language":627,"meta":628,"style":628},"language-csharp shiki shiki-themes github-dark","var db = redis.GetDatabase();\nvar houseId = 1024;\nvar userId = \"u77\";\nvar today = DateTime.Today.ToString(\"yyyy-MM-dd\");\n\n\u002F\u002F 1. 瀏覽次數 +1（String）\nawait db.StringIncrementAsync($\"house:{houseId}:views\");\n\n\u002F\u002F 2. 今日熱門排行 +1，並讓排行榜兩天後自動過期（Sorted Set）\nvar hotKey = $\"hot:houses:{today}\";\nawait db.SortedSetIncrementAsync(hotKey, houseId, 1);\nawait db.KeyExpireAsync(hotKey, TimeSpan.FromDays(2));\n\n\u002F\u002F 3. 加入最近瀏覽，只保留 10 筆（List）\nvar recentKey = $\"recent:{userId}\";\nawait db.ListLeftPushAsync(recentKey, houseId);\nawait db.ListTrimAsync(recentKey, 0, 9);\n\n\u002F\u002F 4. 這個物件是否已收藏（Set）\nvar isFavorite = await db.SetContainsAsync($\"fav:{userId}\", houseId);\n\n\u002F\u002F 首頁：今日熱門前 10 名\nvar top10 = await db.SortedSetRangeByRankWithScoresAsync(hotKey, 0, 9, Order.Descending);\n","csharp","",[56,630,631,657,674,690,715,722,729,760,765,771,796,814,838,843,849,873,886,910,915,921,955,960,966],{"__ignoreMap":628},[632,633,636,640,644,647,651,654],"span",{"class":634,"line":635},"line",1,[632,637,639],{"class":638},"snl16","var",[632,641,643],{"class":642},"svObZ"," db",[632,645,646],{"class":638}," =",[632,648,650],{"class":649},"s95oV"," redis.",[632,652,653],{"class":642},"GetDatabase",[632,655,656],{"class":649},"();\n",[632,658,660,662,665,667,671],{"class":634,"line":659},2,[632,661,639],{"class":638},[632,663,664],{"class":642}," houseId",[632,666,646],{"class":638},[632,668,670],{"class":669},"sDLfK"," 1024",[632,672,673],{"class":649},";\n",[632,675,677,679,682,684,688],{"class":634,"line":676},3,[632,678,639],{"class":638},[632,680,681],{"class":642}," userId",[632,683,646],{"class":638},[632,685,687],{"class":686},"sU2Wk"," \"u77\"",[632,689,673],{"class":649},[632,691,693,695,698,700,703,706,709,712],{"class":634,"line":692},4,[632,694,639],{"class":638},[632,696,697],{"class":642}," today",[632,699,646],{"class":638},[632,701,702],{"class":649}," DateTime.Today.",[632,704,705],{"class":642},"ToString",[632,707,708],{"class":649},"(",[632,710,711],{"class":686},"\"yyyy-MM-dd\"",[632,713,714],{"class":649},");\n",[632,716,718],{"class":634,"line":717},5,[632,719,721],{"emptyLinePlaceholder":720},true,"\n",[632,723,725],{"class":634,"line":724},6,[632,726,728],{"class":727},"sAwPA","\u002F\u002F 1. 瀏覽次數 +1（String）\n",[632,730,732,735,738,741,743,746,749,752,755,758],{"class":634,"line":731},7,[632,733,734],{"class":638},"await",[632,736,737],{"class":649}," db.",[632,739,740],{"class":642},"StringIncrementAsync",[632,742,708],{"class":649},[632,744,745],{"class":686},"$\"house:",[632,747,748],{"class":686},"{",[632,750,751],{"class":649},"houseId",[632,753,754],{"class":686},"}",[632,756,757],{"class":686},":views\"",[632,759,714],{"class":649},[632,761,763],{"class":634,"line":762},8,[632,764,721],{"emptyLinePlaceholder":720},[632,766,768],{"class":634,"line":767},9,[632,769,770],{"class":727},"\u002F\u002F 2. 今日熱門排行 +1，並讓排行榜兩天後自動過期（Sorted Set）\n",[632,772,774,776,779,781,784,786,789,791,794],{"class":634,"line":773},10,[632,775,639],{"class":638},[632,777,778],{"class":642}," hotKey",[632,780,646],{"class":638},[632,782,783],{"class":686}," $\"hot:houses:",[632,785,748],{"class":686},[632,787,788],{"class":649},"today",[632,790,754],{"class":686},[632,792,793],{"class":686},"\"",[632,795,673],{"class":649},[632,797,799,801,803,806,809,812],{"class":634,"line":798},11,[632,800,734],{"class":638},[632,802,737],{"class":649},[632,804,805],{"class":642},"SortedSetIncrementAsync",[632,807,808],{"class":649},"(hotKey, houseId, ",[632,810,811],{"class":669},"1",[632,813,714],{"class":649},[632,815,817,819,821,824,827,830,832,835],{"class":634,"line":816},12,[632,818,734],{"class":638},[632,820,737],{"class":649},[632,822,823],{"class":642},"KeyExpireAsync",[632,825,826],{"class":649},"(hotKey, TimeSpan.",[632,828,829],{"class":642},"FromDays",[632,831,708],{"class":649},[632,833,834],{"class":669},"2",[632,836,837],{"class":649},"));\n",[632,839,841],{"class":634,"line":840},13,[632,842,721],{"emptyLinePlaceholder":720},[632,844,846],{"class":634,"line":845},14,[632,847,848],{"class":727},"\u002F\u002F 3. 加入最近瀏覽，只保留 10 筆（List）\n",[632,850,852,854,857,859,862,864,867,869,871],{"class":634,"line":851},15,[632,853,639],{"class":638},[632,855,856],{"class":642}," recentKey",[632,858,646],{"class":638},[632,860,861],{"class":686}," $\"recent:",[632,863,748],{"class":686},[632,865,866],{"class":649},"userId",[632,868,754],{"class":686},[632,870,793],{"class":686},[632,872,673],{"class":649},[632,874,876,878,880,883],{"class":634,"line":875},16,[632,877,734],{"class":638},[632,879,737],{"class":649},[632,881,882],{"class":642},"ListLeftPushAsync",[632,884,885],{"class":649},"(recentKey, houseId);\n",[632,887,889,891,893,896,899,902,905,908],{"class":634,"line":888},17,[632,890,734],{"class":638},[632,892,737],{"class":649},[632,894,895],{"class":642},"ListTrimAsync",[632,897,898],{"class":649},"(recentKey, ",[632,900,901],{"class":669},"0",[632,903,904],{"class":649},", ",[632,906,907],{"class":669},"9",[632,909,714],{"class":649},[632,911,913],{"class":634,"line":912},18,[632,914,721],{"emptyLinePlaceholder":720},[632,916,918],{"class":634,"line":917},19,[632,919,920],{"class":727},"\u002F\u002F 4. 這個物件是否已收藏（Set）\n",[632,922,924,926,929,931,934,936,939,941,944,946,948,950,952],{"class":634,"line":923},20,[632,925,639],{"class":638},[632,927,928],{"class":642}," isFavorite",[632,930,646],{"class":638},[632,932,933],{"class":638}," await",[632,935,737],{"class":649},[632,937,938],{"class":642},"SetContainsAsync",[632,940,708],{"class":649},[632,942,943],{"class":686},"$\"fav:",[632,945,748],{"class":686},[632,947,866],{"class":649},[632,949,754],{"class":686},[632,951,793],{"class":686},[632,953,954],{"class":649},", houseId);\n",[632,956,958],{"class":634,"line":957},21,[632,959,721],{"emptyLinePlaceholder":720},[632,961,963],{"class":634,"line":962},22,[632,964,965],{"class":727},"\u002F\u002F 首頁：今日熱門前 10 名\n",[632,967,969,971,974,976,978,980,983,986,988,990,992],{"class":634,"line":968},23,[632,970,639],{"class":638},[632,972,973],{"class":642}," top10",[632,975,646],{"class":638},[632,977,933],{"class":638},[632,979,737],{"class":649},[632,981,982],{"class":642},"SortedSetRangeByRankWithScoresAsync",[632,984,985],{"class":649},"(hotKey, ",[632,987,901],{"class":669},[632,989,904],{"class":649},[632,991,907],{"class":669},[632,993,994],{"class":649},", Order.Descending);\n",[15,996,997,998,1001,1002,1005],{},"上面 1～4 是四次網路往返。對延遲敏感時可以用 ",[56,999,1000],{},"IBatch"," 或 ",[56,1003,1004],{},"CreateTransaction()"," 把多個指令一次送出；需要「多個指令中間不被插隊」時，用 Lua Script 讓它們在 Redis 內一次執行完。",[10,1007,1009],{"id":1008},"過期記憶體與持久化","過期、記憶體與持久化",[78,1011,1012],{"id":1012},"過期時間",[15,1014,1015,1016,1001,1019,1022,1023,1026],{},"用 ",[56,1017,1018],{},"EXPIRE",[56,1020,1021],{},"SET ... EX"," 設定的 Key，到期後就會被刪除。Redis 用兩種方式清理：存取時發現過期就刪（惰性刪除），加上背景定期抽樣檢查。所以",[19,1024,1025],{},"過期的 Key 不一定馬上從記憶體消失","，但讀取時一定讀不到。",[78,1028,1029],{"id":1029},"記憶體滿了怎麼辦",[15,1031,1032,1035,1036,1039],{},[56,1033,1034],{},"maxmemory"," 設定 Redis 最多用多少記憶體，用滿之後依 ",[56,1037,1038],{},"maxmemory-policy"," 處理：",[86,1041,1042,1055],{},[89,1043,1044],{},[92,1045,1046,1049,1052],{},[95,1047,1048],{},"策略",[95,1050,1051],{},"行為",[95,1053,1054],{},"適合",[102,1056,1057,1074,1089,1106],{},[92,1058,1059,1065,1071],{},[107,1060,1061,1064],{},[56,1062,1063],{},"noeviction","（預設）",[107,1066,1067,1068],{},"不刪任何東西，",[19,1069,1070],{},"寫入直接報錯",[107,1072,1073],{},"資料不能被刪的情境",[92,1075,1076,1081,1084],{},[107,1077,1078],{},[56,1079,1080],{},"allkeys-lru",[107,1082,1083],{},"從所有 Key 中刪除最久沒用的",[107,1085,1086],{},[19,1087,1088],{},"純快取",[92,1090,1091,1096,1103],{},[107,1092,1093],{},[56,1094,1095],{},"volatile-lru",[107,1097,1098,1099,1102],{},"只從",[19,1100,1101],{},"有設過期時間","的 Key 中刪最久沒用的",[107,1104,1105],{},"快取與重要資料混放",[92,1107,1108,1116,1119],{},[107,1109,1110,132,1113],{},[56,1111,1112],{},"allkeys-lfu",[56,1114,1115],{},"volatile-lfu",[107,1117,1118],{},"依使用頻率刪除",[107,1120,1121],{},"有明顯熱點的快取",[15,1123,1124,1125,1127,1128,1130],{},"純粹當快取用的 Redis，記得改成 ",[56,1126,1080],{},"；用預設的 ",[56,1129,1063],{},"，記憶體滿的那一刻，所有寫入都會失敗。",[78,1132,1133],{"id":1133},"持久化",[15,1135,1136],{},"Redis 重啟時，記憶體的資料要從磁碟載回來，有兩種方式：",[24,1138,1139,1148],{},[27,1140,1141,1144,1145],{},[19,1142,1143],{},"RDB","：定期把整個記憶體快照存成一個檔案。檔案小、重啟快，但",[19,1146,1147],{},"兩次快照之間的資料會遺失",[27,1149,1150,1153,1154,1157],{},[19,1151,1152],{},"AOF","：把每個寫入指令記錄下來，",[56,1155,1156],{},"appendfsync everysec"," 時最多遺失 1 秒的資料，檔案比較大，需要定期重寫壓縮",[15,1159,1160],{},"兩者可以同時開啟。如果 Redis 只是快取，掉了可以從資料庫重建，RDB 就夠了；如果裡面有不能遺失的資料（例如計數器還沒寫回資料庫），就要開 AOF。",[10,1162,1163],{"id":1163},"上線前要知道的事",[24,1165,1166,1179,1200,1206,1220,1226,1232],{},[27,1167,1168,1174,1175,1178],{},[19,1169,1170,1171],{},"不要在正式環境下 ",[56,1172,1173],{},"KEYS *","：它會掃過所有的 Key，幾百萬個 Key 時要好幾秒，這段時間 Redis 完全無法處理其他請求，所有服務一起逾時。要找 Key 用 ",[56,1176,1177],{},"SCAN","，它每次只掃一小部分",[27,1180,1181,1184,1185,1188,1189,1188,1192,1195,1196,1199],{},[19,1182,1183],{},"小心 Big Key","：一個 Hash 裡有幾百萬個欄位、一個 List 有幾十萬筆，",[56,1186,1187],{},"HGETALL","、",[56,1190,1191],{},"LRANGE 0 -1",[56,1193,1194],{},"SMEMBERS"," 這種讀取全部的指令會很慢，刪除它也會卡住（改用 ",[56,1197,1198],{},"UNLINK"," 在背景刪除）。資料量大的集合要拆成多個 Key",[27,1201,1202,1205],{},[19,1203,1204],{},"小心 Hot Key","：所有請求都打同一個 Key，例如首頁的熱門排行榜，在 Cluster 模式下會集中在同一個節點上。可以在應用程式加一層短時間的本機快取",[27,1207,1208,1211,1212,1215,1216,1219],{},[19,1209,1210],{},"Key 要有命名規則","：用 ",[56,1213,1214],{},":"," 分層，例如 ",[56,1217,1218],{},"{服務}:{實體}:{ID}:{用途}","，在監控工具裡才找得到、才知道是誰放的",[27,1221,1222,1225],{},[19,1223,1224],{},"快取的 Key 一定要設過期時間","：沒有過期時間的 Key 只會越來越多，直到記憶體用滿",[27,1227,1228,1231],{},[19,1229,1230],{},"不要把 Redis 當成唯一的資料來源","：即使開了 AOF，Redis 也不是設計來取代關聯式資料庫的。重要的資料最終還是要落地到資料庫",[27,1233,1234,1237,1238,1241,1242,1245],{},[19,1235,1236],{},"連線要共用","：StackExchange.Redis 的 ",[56,1239,1240],{},"ConnectionMultiplexer"," 設計上就是",[19,1243,1244],{},"整個應用程式共用一個","，註冊成 Singleton，不要每次請求都建立新連線",[10,1247,1248],{"id":1248},"一句話總結",[15,1250,1251,1252,1255],{},"Redis 是記憶體中的資料結構伺服器：String 做計數與快取、Hash 存物件欄位、List 做最近 N 筆、Set 做不重複與交集、Sorted Set 做排行與延遲任務，每個指令都是原子的；但也因為單執行緒，",[56,1253,1254],{},"KEYS","、Big Key 這種慢指令會拖垮所有請求，上線前要設定好過期時間、淘汰策略與持久化方式。",[1257,1258,1259],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":628,"searchDepth":659,"depth":659,"links":1261},[1262,1263,1271,1272,1277,1278],{"id":12,"depth":659,"text":13},{"id":69,"depth":659,"text":69,"children":1264},[1265,1266,1267,1268,1269,1270],{"id":80,"depth":676,"text":81},{"id":201,"depth":676,"text":202},{"id":292,"depth":676,"text":293},{"id":384,"depth":676,"text":385},{"id":480,"depth":676,"text":481},{"id":588,"depth":676,"text":588},{"id":617,"depth":659,"text":618},{"id":1008,"depth":659,"text":1009,"children":1273},[1274,1275,1276],{"id":1012,"depth":676,"text":1012},{"id":1029,"depth":676,"text":1029},{"id":1133,"depth":676,"text":1133},{"id":1163,"depth":659,"text":1163},{"id":1248,"depth":659,"text":1248},"2023-02-21",null,"md",{},"\u002Farticles\u002Fredis-data-structures-use-cases",{"title":5,"description":628},"articles\u002Fredis-data-structures-use-cases",[1287,175,1288,1289],"Redis","資料結構","系統設計","76avnB3SkQPDQM3-21hDsuni5l01ya4In--WAsSvPmQ",1792959150545]