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

การใช้งาน redis-cli

ทดสอบบน Redis 8.10.2 (redis:8.10.2-alpine, redis-cli 8.10.2 จาก image เดียวกัน) บน Mac (Apple silicon) output เป็นของจริง; ตรงที่ output ขึ้นกับว่ามี terminal ต่ออยู่หรือไม่ บทจะบอกไว้

redis-cli คือ command-line interface ที่มากับทุกการติดตั้ง Redis (และมีใน official Docker image) รองรับสองโหมด: interactive (prompt แบบ REPL) และ inline (ส่งคำสั่งเดียวตรงจาก shell)

flag ที่ใช้บ่อย:

Flagค่าเริ่มต้นวัตถุประสงค์
-h host127.0.0.1hostname หรือ IP ของ Redis server
-p port6379port ของ Redis server
-n db-number0เลือกหมายเลข database (0–15) ตอนเชื่อมต่อ
-u uri—ใช้ URI เดียวแทนสาม flag: redis://user:pass@host:6379/1 (rediss:// สำหรับ TLS)
-3RESP2ใช้ RESP3 (เหมือนส่ง HELLO 3 ก่อน)
scripts/intro-redis-cli-connect.sh
# Connect to the default host and port
redis-cli
# Connect explicitly — useful when targeting a remote server
redis-cli -h 127.0.0.1 -p 6379
# Connect and immediately switch to database 1
redis-cli -n 1
# The same thing as one URI; -u redis://127.0.0.1:6379/1 DBSIZE ran against the real server and printed 0
redis-cli -u redis://127.0.0.1:6379/1 DBSIZE

ใน inline mode คุณส่งคำสั่งเดียวเป็น argument ให้ redis-cli แล้วผลลัพธ์จะพิมพ์ออก stdout — สะดวกสำหรับ script:

scripts/intro-redis-cli-inline.sh
redis-cli PING

ใน interactive mode คุณรัน redis-cli โดยไม่มี argument ของคำสั่ง แล้วได้ prompt 127.0.0.1:6379> ให้พิมพ์คำสั่งทีละคำสั่ง

Reply เดียวกันแต่พิมพ์ต่างกัน ขึ้นกับว่าออกไปที่ไหน

หัวข้อที่มีชื่อว่า “Reply เดียวกันแต่พิมพ์ต่างกัน ขึ้นกับว่าออกไปที่ไหน”

นี่คือสิ่งที่มีประโยชน์ที่สุดเกี่ยวกับ redis-cli และเป็นต้นเหตุของ shell script พังมากมาย เมื่อ stdout เป็น terminal redis-cli จะพิมพ์ reply แบบสวยงาม ((nil), (integer) 0, เครื่องหมายคำพูด, รายการมีเลขลำดับ) เมื่อ stdout ไม่ใช่ terminal (pipe, $(...), ไฟล์, การเรียกแบบ argument ใน script) มันจะพิมพ์ค่า raw แทน เซิร์ฟเวอร์เดียวกัน คำสั่งเดียวกัน:

# a terminal, or --no-raw # inside a pipe / $(...) / script (default)
"v1" <- GET k1 v1
(nil) <- GET nokey (an empty line)
(integer) 0 <- DEL nokey 0
1) "v1" v1
2) (nil) <- MGET k1 nokey (an empty line)

บังคับได้ทั้งสองทาง: --no-raw ให้รูปแบบสวยแม้ถูก pipe, --raw ให้รูปแบบ raw แม้บน terminal (harness ตรวจสอบของคอร์สนี้ใช้ --no-raw เพื่อให้ transcript ตรงกับที่เห็นที่ prompt) โหมดผลลัพธ์อีกแบบที่ควรรู้:

$ redis-cli RPUSH list a "b c" d # then:
$ redis-cli --csv LRANGE list 0 -1
"a","b c","d"

PING คือการตรวจสอบที่เบาที่สุดว่าเข้าถึงเซิร์ฟเวอร์ได้:

cli/intro-redis-cli-1.redis
127.0.0.1:6379> PING
PONG

ส่งข้อความเพิ่มได้ Redis จะ echo กลับ — มีประโยชน์ทดสอบ round-trip latency ใน script:

cli/intro-redis-cli-2.redis
127.0.0.1:6379> PING "hello world"
"hello world"

ECHO คืน argument กลับมาตามเดิม ใช้หลัก ๆ เพื่อทดสอบว่า client encode string ถูกต้อง:

cli/intro-redis-cli-3.redis
127.0.0.1:6379> ECHO "Hello"
"Hello"

Redis มี 16 logical database หมายเลข 0 ถึง 15 ใช้ process และหน่วยความจำเดียวกันแต่ keyspace แยกกันสมบูรณ์ ค่าเริ่มต้นคือ database 0 ต่อใน session เดิม:

cli/intro-redis-cli-4.redis
127.0.0.1:6379> SELECT 1
OK
127.0.0.1:6379[1]> SET greeting "hi from db1"
OK
127.0.0.1:6379[1]> SELECT 0
OK
127.0.0.1:6379> GET greeting
(nil)

สังเกตว่า prompt เปลี่ยนเป็น 127.0.0.1:6379[1] เมื่ออยู่ใน database ที่ไม่ใช่ 0

DBSIZE คืนจำนวน key ใน database ที่เลือกอยู่ session ที่ผ่านมาเขียนแค่ใน database 1 ดังนั้น database 0 ว่าง:

cli/intro-redis-cli-5.redis
127.0.0.1:6379> DBSIZE
(integer) 0
127.0.0.1:6379> SET a 1
OK
127.0.0.1:6379> SET b 2
OK
127.0.0.1:6379> DBSIZE
(integer) 2

FLUSHDB ลบทุก key ใน database ปัจจุบัน ย้อนกลับไม่ได้ — ใช้เฉพาะ development หรือ test เท่านั้น มีตัวเลือก ASYNC (คืนหน่วยความจำเบื้องหลัง) และ SYNC:

cli/intro-redis-cli-6.redis
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> DBSIZE
(integer) 0
127.0.0.1:6379> FLUSHDB ASYNC
OK

FLUSHALL ล้างทุก database ไม่ใช่แค่ปัจจุบัน: key greeting ใน database 1 รอดจาก FLUSHDB ข้างบน (มันล้างแค่ database 0) และจะหายก็ต่อเมื่อ FLUSHALL

redis-cli มีเอกสารในตัว ใช้ HELP ตามด้วยชื่อคำสั่งหรือ tag ของหมวด HELP SET ที่ prompt แบบ interactive พิมพ์ว่า (ของจริงจาก 8.10.2):

SET key value [NX|XX|IFEQ ifeq-value|IFNE ifne-value|IFDEQ ifdeq-digest|IFDNE ifdne-digest] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]
summary: Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.
since: 1.0.0
group: string

ตัวเลือก IFEQ|IFNE|IFDEQ|IFDNE เพิ่งมาใน Redis 8.4 — redis-cli เก่าบนเครื่องคุณที่คุยกับเซิร์ฟเวอร์ใหม่จะไม่ autocomplete ให้ HELP @string แสดงคำสั่งทุกตัวในหมวด string (เริ่มด้วย APPEND, DECR, DECRBY, DELEX, DIGEST, GET, GETDEL, GETEX, …) พร้อม argument และ version ที่ระบุใน since: — วิธีเร็วในการดูว่าคำสั่งมีบนเซิร์ฟเวอร์ที่คุณรันอยู่หรือไม่

PING
ECHO "Hello"
SELECT 1
DBSIZE
SELECT 0
scripts/intro-redis-cli-flags.sh
redis-cli -r 3 -i 0.2 INCR hits # repeat 3 times, 0.2 s apart -> 1 2 3
echo -n "from stdin" | redis-cli -x SET note # -x: last argument read from stdin
redis-cli -e LPUSH note q # -e: exit code 1 on an error reply
redis-cli --scan --pattern 'user:*' # SCAN in a loop, never KEYS (next module)
redis-cli --latency # live min/avg/max round-trip until Ctrl-C
printf 'SET a 1\r\nINCR a\r\nLPUSH a x\r\n' | redis-cli --pipe # mass insert

ผลจริง: -r 3 -i 0.2 INCR พิมพ์ 1 2 3 -x SET note แล้ว GET note ได้ from stdin -e LPUSH บน key string นั้นพิมพ์ WRONGTYPE Operation against a key holding the wrong kind of value และ exit ด้วย 1; คำสั่งเดียวกัน ไม่มี -e exit 0 — shell script ที่ไม่สนใจเรื่องนี้จึงไม่รู้ว่าล้มเหลว --latency พิมพ์ 0.059 1.396 0.143 89 (min, max, avg ms, จำนวน sample) หลังผ่านไป 3 วินาทีบน container และ --pipe ส่งสามคำสั่ง โดยตัวที่สามตั้งใจให้ error เรื่อง wrong-type:

WRONGTYPE Operation against a key holding the wrong kind of value
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 1, replies: 3

--pipe คือ bulk loader ระดับ protocol ที่จะพบอีกในบท Pipelines สังเกตการจบบรรทัดแบบ \r\n: มันพูด inline command แบบ raw RESP

HELLO 3 สลับ connection เป็น RESP3 และคืนข้อมูลของเซิร์ฟเวอร์:

127.0.0.1:6379> HELLO 3
1# "server" => "redis"
2# "version" => "8.10.2"
3# "proto" => (integer) 3
4# "id" => (integer) 43
...

(ตัดให้สั้น: reply ต่อด้วย mode, role และ array modules; id ของคุณจะต่างออกไป) RESP3 ให้ reply ที่มี type — map ยังเป็น map, HGETALL บน key ที่ไม่มีได้ map ว่างแทน array ว่าง — และทำให้ connection ที่ subscribe อยู่รันคำสั่งปกติได้ด้วย (บท Pub/Sub อธิบายว่าทำไมสำคัญ) คอร์สนี้ใช้ RESP2 เริ่มต้นใน CLI; ioredis 6 เจรจาด้วย HELLO ตอนเชื่อมต่อ

AI เขียน health-check script สำหรับ deploy pipeline:

🐛 หา bug ในโค้ดนี้

#!/bin/bash
# fail the deploy if the session key is missing
val=$(redis-cli GET session:42)
if [ "$val" = "(nil)" ]; then
echo "no session" >&2
exit 1
fi
echo "session ok"
เฉลย

string (nil) ปรากฏเฉพาะตอน redis-cli เขียนไปที่ terminal ภายใน $(...) stdout เป็น pipe redis-cli จึงพิมพ์ output แบบ raw — สำหรับ key ที่ไม่มี นั่นคือ บรรทัดว่าง เรารันกับ key ที่ไม่มี: script เข้า branch “session found” พร้อม [] คือผ่านไปเงียบ ๆ วิธีแก้คือทดสอบสิ่งที่ script ได้รับจริง หรือดีกว่านั้นคือถามคำถามที่ตอบเป็นจำนวนเต็มชัดเจน:

scripts/intro-redis-cli-check.sh
#!/bin/bash
# EXISTS returns 1 or 0 in raw mode; -e fails the script on server errors
n=$(redis-cli -e EXISTS session:42) || exit 2
if [ "$n" -eq 0 ]; then
echo "no session" >&2
exit 1
fi
echo "session ok"

เมื่อ key ไม่มี script แรกพิมพ์ session found: [] ส่วน [ -z "$val" ] หรือการตรวจด้วย EXISTS พิมพ์ “no session” ทั้งคู่ — วิธีแก้ใช้ได้

redis-cli คือ protocol client ที่มีสองบุคลิก: แบบมนุษย์ (terminal — มี label, เครื่องหมายคำพูด, (nil)) และแบบเครื่อง (pipe — ค่าเปล่า ๆ, บรรทัดว่าง) ทุกอย่างที่คัดลอกจาก transcript ไปใส่ script ต้องอ่านใหม่ในภาษาแบบเครื่อง และมันเป็น client แบบ บาง: ไม่มี retry ไม่มี pool ไม่มี type — ไว้ดู Redis ไม่ใช่ไว้ให้แอปคุยกับ Redis

ตัวเลือกข้อดีข้อเสีย
ใช้ redis-cli แบบ interactiveทดสอบคำสั่งเร็ว ได้ feedback ทันที เหมาะกับการ debugไม่มี connection pooling, retry หรือ error handling ที่แอปจริงต้องมี
ใช้ client library ในโค้ดแอปจัดการ connection lifecycle, pipelining และ retry ให้อัตโนมัติต้องเขียนโค้ดเพิ่ม และมี dependency เพิ่ม
สลับ database ด้วย SELECTสะดวกในการแยกข้อมูลชั่วคราวตอน devไม่ใช่ multi-tenancy จริง — ทุก database ยังใช้ instance, หน่วยความจำ และประสิทธิภาพร่วมกัน
parse output ของ redis-cli ใน shell scriptไม่ต้องมี dependency นอกจาก CLIoutput ต่างกันระหว่าง terminal กับ pipe; error reply exit 0 ถ้าไม่ใส่ -e
  • “สิ่งที่เห็นที่ prompt คือสิ่งที่ $(redis-cli ...) คืนมา” ไม่ใช่: (nil), (integer), เครื่องหมายคำพูดและเลขลำดับหายไปเมื่อ stdout ไม่ใช่ terminal
  • “คำสั่งที่ล้มเหลวทำให้ redis-cli exit ค่าไม่ใช่ศูนย์” เฉพาะเมื่อใส่ -e; ตัวอย่าง WRONGTYPE exit 0 ถ้าไม่ใส่
  • “SELECT ให้ Redis server แยกกัน” process เดียว หน่วยความจำเดียว FLUSHALL เดียว
  • “-a คือวิธีส่งรหัสผ่านที่ปลอดภัย” มันรั่วใน process list; ใช้ REDISCLI_AUTH หรือ -u จาก secret store
  • เชื่อมต่อ redis-cli เข้า production โดยตรงเพื่อรันคำสั่งแก้ไข — พิมพ์ผิดตัวเดียวอาจกลายเป็น FLUSHDB หรือ DEL บน key จริง ใช้ script ที่ผ่านการ review หรือ client library แทนเมื่อต้องแตะข้อมูล production
  • คิดว่า SELECT แยก tenant ได้ปลอดภัย — 16 logical database ยังใช้ CPU และหน่วยความจำร่วมกัน และ FLUSHALL ครั้งเดียวกระทบทั้งหมด ไม่ใช่ตัวแทนของการแยก tenant จริง
  • มอง redis-cli เป็นช่องทางเดียวที่แอปคุยกับ Redis — รูปแบบที่ดีคือ redis-cli สำหรับ debug/สำรวจเท่านั้น ส่วนโค้ด production ผ่าน client library ที่จัดการ connection pooling และ retry
flag ไหนเลือก Redis database ที่ต้องการตอนเชื่อมต่อด้วย redis-cli?
PING คืนอะไรเมื่อเข้าถึงเซิร์ฟเวอร์ได้?
FLUSHDB ทำอะไร?
จะแสดงคำสั่งทั้งหมดในหมวด string จากภายใน redis-cli ได้อย่างไร?
ใน bash script, val=$(redis-cli GET missing) — $val มีค่าเป็นอะไร?
คำสั่งใน script ล้มเหลวด้วย WRONGTYPE exit code ของ redis-cli LPUSH str x และ redis-cli -e LPUSH str x คืออะไร?