ข้ามไปยังเนื้อหา

SCAN & Key Operations

คำสั่งเหล่านี้ทำงานได้กับ key ทุกตัวไม่ว่าจะเป็น type ใด

คืน 1 ถ้า key มีอยู่ และคืน 0 ถ้าไม่มี คุณสามารถเช็กหลาย key พร้อมกันได้ — ค่าที่คืนกลับมาคือจำนวน key ที่มีอยู่

127.0.0.1:6379> SET user:1 "Ada"
OK
127.0.0.1:6379> EXISTS user:1
(integer) 1
127.0.0.1:6379> EXISTS user:99
(integer) 0
127.0.0.1:6379> EXISTS user:1 user:99 user:1
(integer) 2

ลบ key หนึ่งตัวหรือมากกว่า คืนจำนวน key ที่ถูกลบจริง ๆ (key ที่ไม่มีอยู่จะไม่ถูกนับ)

127.0.0.1:6379> SET a "1"
OK
127.0.0.1:6379> SET b "2"
OK
127.0.0.1:6379> DEL a b nonexistent
(integer) 2
127.0.0.1:6379> EXISTS a
(integer) 0

คืน type ของข้อมูลที่เก็บไว้ที่ key หนึ่ง: string, list, set, zset, hash หรือ stream

127.0.0.1:6379> SET mystr "hello"
OK
127.0.0.1:6379> TYPE mystr
string
127.0.0.1:6379> TYPE nonexistent:key
none

เปลี่ยนชื่อ key ถ้า key ปลายทางมีอยู่แล้ว จะถูกเขียนทับ ใช้ RENAMENX เพื่อเปลี่ยนชื่อเฉพาะกรณีที่ปลายทางยังไม่มีอยู่

127.0.0.1:6379> SET old:key "value"
OK
127.0.0.1:6379> RENAME old:key new:key
OK
127.0.0.1:6379> EXISTS old:key
(integer) 0
127.0.0.1:6379> GET new:key
"value"
SET user:1 "Ada"
EXISTS user:1
EXISTS user:99
SET a "1"
SET b "2"
DEL a b nonexistent
SET mystr "hello"
TYPE mystr
SET old:key "value"
RENAME old:key new:key
GET new:key

KEYS pattern คืน key ทุกตัวที่ตรงกับ pattern แบบ glob สะดวกมากสำหรับการพัฒนาและ debug

127.0.0.1:6379> MSET user:1 "Ada" user:2 "Bob" session:abc "tok" session:xyz "tok2"
OK
127.0.0.1:6379> KEYS user:*
1) "user:2"
2) "user:1"
127.0.0.1:6379> KEYS *
1) "user:2"
2) "user:1"
3) "session:abc"
4) "session:xyz"

KEYS เป็นการ scan ทั้ง keyspace แบบ blocking และ O(N) บน Redis server ที่มี key เป็นล้านตัว คำสั่งนี้อาจใช้เวลาหลายวินาที และ block client ตัวอื่นทั้งหมดตลอดช่วงนั้น อย่ารัน KEYS กับ production server เด็ดขาด — ใช้ SCAN แทน

SCAN เป็น iterator ที่ใช้ cursor การเรียกแต่ละครั้งจะประมวลผล key เป็น batch เล็ก ๆ และคืนค่ากลับมา:

  1. cursor ตัวใหม่ (เพื่อส่งต่อให้การเรียกครั้งถัดไป)
  2. รายการ key จาก batch นี้

เมื่อ cursor ที่คืนกลับมาเป็น 0 แสดงว่าการวนไล่เสร็จสมบูรณ์

127.0.0.1:6379> MSET k1 a k2 b k3 c k4 d k5 e k6 f k7 g k8 h k9 i k10 j
OK
127.0.0.1:6379> SCAN 0 MATCH k* COUNT 3
1) "7"
2) 1) "k9"
2) "k3"
3) "k1"
127.0.0.1:6379> SCAN 7 MATCH k* COUNT 3
1) "4"
2) 1) "k8"
2) "k2"
127.0.0.1:6379> SCAN 4 MATCH k* COUNT 3
1) "0"
2) 1) "k10"
2) "k4"
3) "k7"
4) "k5"
5) "k6"

COUNT เป็นเพียง คำใบ้ ไม่ใช่ขีดจำกัดตายตัว — Redis อาจคืน key มากกว่าหรือน้อยกว่านั้นในแต่ละครั้ง การวนไล่จะเสร็จสมบูรณ์เสมอเมื่อ cursor กลับมาเป็น 0

MSET k1 a k2 b k3 c k4 d k5 e k6 f k7 g k8 h k9 i k10 j
SCAN 0 MATCH k* COUNT 3
SCAN 0 MATCH k* COUNT 100
Optionคำอธิบาย
MATCH patternกรองผลลัพธ์ด้วย pattern แบบ glob (การกรองเกิดขึ้นหลังการสุ่มตัวอย่าง)
COUNT hintแนะนำว่าจะสุ่มตัวอย่างกี่ item ต่อการเรียกหนึ่งครั้ง (ค่าเริ่มต้น 10)
TYPE typeคืนเฉพาะ key ที่เป็น type ที่ระบุ (Redis 6.0+)

loop สำหรับ scan ทั้งหมดที่ใช้จริงใน script จะมีหน้าตาประมาณนี้:

127.0.0.1:6379> SCAN 0 MATCH session:* COUNT 100
1) "42"
2) 1) "session:abc"
127.0.0.1:6379> SCAN 42 MATCH session:* COUNT 100
1) "0"
2) 1) "session:xyz"

เรียก SCAN ด้วย cursor ที่คืนกลับมาเรื่อย ๆ จนกว่า cursor จะกลับมาเป็น 0 แล้วรวบรวมผลลัพธ์จากทุกรอบเข้าด้วยกัน

SCAN 0 MATCH session:* COUNT 100
ตัวเลือกBenefitCost
SCAN (cursor-based iteration)ไม่ block server แบ่งงานเป็น batch เล็ก ๆ ปลอดภัยกับ productionไม่ได้ snapshot ที่แน่นอน — key ที่ถูกเพิ่ม/ลบระหว่าง scan อาจโผล่มาซ้ำหรือถูกข้าม
KEYS * (full blocking scan)ง่าย เรียกครั้งเดียวได้ผลลัพธ์ครบ เหมาะกับ dataset เล็กใน devblock event loop จนกว่าจะสแกนครบทุก key ห้ามใช้กับ production ที่มี key จำนวนมาก
ตั้ง COUNT สูงใน SCAN เพื่อลดจำนวนรอบเรียก round-trip น้อยลง เร็วขึ้นในภาพรวมแต่ละรอบทำงานหนักขึ้น อาจกระทบ latency ของคำสั่งอื่นที่รันคั่นอยู่
  • ใช้ KEYS * เพื่อดู key ทั้งหมดบน production — คำสั่งนี้ block server จนกว่าจะสแกนครบทุก key บน dataset ใหญ่อาจทำให้ client อื่นค้างหลายวินาที ควรใช้ SCAN แทนเสมอ
  • เข้าใจผิดว่า COUNT คือขีดจำกัดตายตัวของจำนวน key ที่คืนมาCOUNT เป็นเพียงคำใบ้ Redis อาจคืน key มากกว่าหรือน้อยกว่านั้นในแต่ละรอบก็ได้
  • หยุด loop การเรียก SCAN ก่อนที่ cursor จะกลับมาเป็น 0 — ทำให้ได้ผลลัพธ์ไม่ครบ การวนไล่ต้องเรียกต่อเนื่องจนกว่า cursor ที่คืนมาจะเป็น 0 เท่านั้นถึงจะถือว่าสมบูรณ์

💡 ตัวอย่างจากของจริง

GitHub-style rate-limit dashboards — ใช้ SCAN แทน KEYS เมื่อต้อง audit key ที่ตรงกับ pattern บน production Redis instance ที่มี key นับล้านตัว เพื่อไม่ให้กระทบ traffic จริง

Cache invalidation tooling — ทีม ops มักเขียน script ที่ loop SCAN เพื่อหา key ที่ตรงกับ pattern (เช่น cache:user:*) แล้วลบเป็น batch แทนที่จะเสี่ยง block server ด้วย KEYS

SCAN คืนค่าอะไรเมื่อการวนไล่ทั้งหมดเสร็จสมบูรณ์?
ทำไม KEYS ถึงอันตรายบน production Redis server?
EXISTS user:1 user:1 user:2 คืนค่าอะไรถ้า user:1 มีอยู่ แต่ user:2 ไม่มี?
TYPE คืนค่าอะไรสำหรับ key ที่ไม่มีอยู่?