Lists
Redis List คืออะไร?
หัวข้อที่มีชื่อว่า “Redis List คืออะไร?”Redis List คือคอลเลกชันสตริงที่เรียงลำดับ โดยนำไปใช้งานในรูปแบบ doubly linked list ทำให้สามารถ push และ pop ที่ปลายทั้งสองด้านได้ด้วยความซับซ้อน O(1) เหมาะสำหรับ queue, stack และ activity feed ข้อที่ต้องระวังคือการเข้าถึงองค์ประกอบตรงกลางมีความซับซ้อน O(N) ดังนั้น List จึงเหมาะที่สุดเมื่อทำงานกับปลายด้านหัวหรือท้ายเป็นหลัก
LPUSH และ RPUSH
หัวข้อที่มีชื่อว่า “LPUSH และ RPUSH”LPUSH key val [val ...] เพิ่มค่าหนึ่งค่าหรือหลายค่าไปที่ด้านหัวของ list เมื่อ push หลายค่าพร้อมกัน อาร์กิวเมนต์ตัวสุดท้ายจะอยู่ที่หัว เพราะแต่ละค่าถูก push ทีละตัวจากซ้ายไปขวา
RPUSH key val [val ...] เพิ่มค่าหนึ่งค่าหรือหลายค่าไปที่ด้านท้ายของ list
127.0.0.1:6379> RPUSH tasks "buy milk"(integer) 1127.0.0.1:6379> RPUSH tasks "write code" "review PR"(integer) 3127.0.0.1:6379> LPUSH tasks "urgent: fix bug"(integer) 4RPUSH tasks "buy milk"
RPUSH tasks "write code" "review PR"
LPUSH tasks "urgent: fix bug"
LRANGE tasks 0 -1
LLEN tasks
LINDEX tasks 0
LINDEX tasks -1LRANGE, LLEN, และ LINDEX
หัวข้อที่มีชื่อว่า “LRANGE, LLEN, และ LINDEX”เมื่อมี list แล้ว คำสั่งสามตัวนี้ครอบคลุมรูปแบบการอ่านข้อมูลส่วนใหญ่:
LRANGE key start stop— คืนค่าองค์ประกอบตั้งแต่ indexstartถึงstop(รวมปลายทั้งสอง นับจาก 0) ใช้-1สำหรับองค์ประกอบสุดท้าย-2สำหรับตัวที่สองจากท้ายLLEN key— คืนจำนวนองค์ประกอบใน listLINDEX key index— คืนค่าองค์ประกอบเดียวที่ index ที่กำหนด index ลบนับจากท้าย
127.0.0.1:6379> LRANGE tasks 0 -11) "urgent: fix bug"2) "buy milk"3) "write code"4) "review PR"127.0.0.1:6379> LLEN tasks(integer) 4127.0.0.1:6379> LINDEX tasks 0"urgent: fix bug"127.0.0.1:6379> LINDEX tasks -1"review PR"LRANGE tasks 0 -1 คือวิธี idiomatic สำหรับดึงข้อมูลทุกองค์ประกอบใน list ส่วน LINDEX มีความซับซ้อน O(N) เพราะต้องเดินผ่าน list จากปลายที่ใกล้ที่สุด ควรใช้เฉพาะกับตำแหน่งแรกหรือท้ายไม่กี่ตำแหน่งเท่านั้น
LSET และ LREM
หัวข้อที่มีชื่อว่า “LSET และ LREM”LSET key index value เขียนทับองค์ประกอบที่ index ที่กำหนด list ต้องมีองค์ประกอบอยู่ที่ตำแหน่งนั้นแล้ว มิฉะนั้น Redis จะคืน error
LREM key count value ลบ value ที่พบใน list:
count > 0— ลบได้ไม่เกินcountครั้ง โดยสแกนจากหัวไปท้ายcount < 0— ลบได้ไม่เกิน|count|ครั้ง โดยสแกนจากท้ายไปหัวcount = 0— ลบ ทุก การปรากฏ
127.0.0.1:6379> LSET tasks 1 "buy oat milk"OK127.0.0.1:6379> LRANGE tasks 0 -11) "urgent: fix bug"2) "buy oat milk"3) "write code"4) "review PR"127.0.0.1:6379> RPUSH tasks "write code"(integer) 5127.0.0.1:6379> LREM tasks 0 "write code"(integer) 2RPUSH tasks "buy milk"
RPUSH tasks "write code" "review PR"
LPUSH tasks "urgent: fix bug"
LSET tasks 1 "buy oat milk"
LRANGE tasks 0 -1
RPUSH tasks "write code"
LREM tasks 0 "write code"
LRANGE tasks 0 -1LTRIM — เก็บเฉพาะช่วงที่ต้องการ
หัวข้อที่มีชื่อว่า “LTRIM — เก็บเฉพาะช่วงที่ต้องการ”LTRIM key start stop แก้ไข list ในที่ โดยเก็บเฉพาะองค์ประกอบใน [start, stop] และทิ้งทุกอย่างที่อยู่นอกช่วงนั้น คืนค่า OK ทันที ทำให้เรียกได้อย่างปลอดภัยหลังการเขียนทุกครั้ง
127.0.0.1:6379> DEL recent(integer) 1127.0.0.1:6379> RPUSH recent "page-A" "page-B" "page-C" "page-D" "page-E"(integer) 5127.0.0.1:6379> LTRIM recent 0 2OK127.0.0.1:6379> LRANGE recent 0 -11) "page-A"2) "page-B"3) "page-C"DEL recent
RPUSH recent "page-A" "page-B" "page-C" "page-D" "page-E"
LRANGE recent 0 -1
LTRIM recent 0 2
LRANGE recent 0 -1ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
| Redis List (doubly linked list) | push/pop ที่ปลายทั้งสองด้าน O(1) เหมาะกับ queue/stack/feed | เข้าถึงองค์ประกอบกลาง list ด้วย LINDEX เป็น O(N) |
LPUSH + LTRIM สำหรับ recent-items feed | เก็บ list ขนาดคงที่อัตโนมัติ ไม่ต้องเขียน cleanup job แยก | ต้องเรียก LTRIM ทุกครั้งหลัง push ถ้าลืมจะโตไม่มีที่สิ้นสุด |
LREM ลบตาม value | ลบข้อมูลที่ไม่รู้ index ล่วงหน้าได้ | สแกน list ทั้งเส้น O(N) ไม่เหมาะกับ list ขนาดใหญ่ที่ลบบ่อย |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- ใช้
LINDEXหรือวนLRANGEทีละ index เพื่ออ่านข้อมูลกลาง list บ่อย ๆ — แต่ละครั้งเป็น O(N) ถ้าต้องอ่านสุ่มตำแหน่งบ่อย ควรพิจารณาโครงสร้างข้อมูลอื่นแทน - ใช้
LPUSHเก็บ activity feed โดยไม่เรียกLTRIMตามหลัง — list โตไม่มีขีดจำกัดจนกิน memory ทั้งที่ต้องการแค่ N รายการล่าสุด - คาดหวังว่า
LSETทำงานเหมือน “insert ที่ index” — แต่จริง ๆ แล้วLSETเขียนทับ (overwrite) องค์ประกอบที่มีอยู่แล้วเท่านั้น ถ้า index ไม่มีอยู่จริงจะได้ error
💡 ตัวอย่างจากของจริง
Twitter (X) — ใช้ Redis List คู่กับ
LTRIMเก็บ timeline cache ของแต่ละ user ให้มีขนาดคงที่ ไม่ให้โตไม่มีที่สิ้นสุดเมื่อมี tweet ใหม่เข้ามาเรื่อย ๆInstagram — ใช้ List pattern คล้ายกันเก็บ recent activity feed ต่อ user ผสาน
LPUSHสำหรับ event ใหม่และLTRIMตัดให้เหลือ N รายการล่าสุดเสมอ