Counters
จำนวนเต็มที่เก็บเป็น string
หัวข้อที่มีชื่อว่า “จำนวนเต็มที่เก็บเป็น string”Redis เก็บทุกอย่างเป็น string แต่รู้ได้ว่าเมื่อไรที่ string เป็นจำนวนเต็ม (หรือ float) ที่ถูกต้อง และให้คุณทำเลขคณิตกับค่านั้นได้ผ่านคำสั่งเฉพาะ จุดสำคัญคือ operation เหล่านี้เป็น atomic — Redis ทำแต่ละคำสั่งเป็นขั้นตอนเดียวที่ขัดจังหวะไม่ได้
INCR และ DECR
หัวข้อที่มีชื่อว่า “INCR และ DECR”INCR key เพิ่มค่าจำนวนเต็มขึ้นทีละ 1 ส่วน DECR key ลดลงทีละ 1 ถ้า key ไม่มีอยู่ Redis จะถือว่าค่าที่หายไปนั้นเป็น 0 ก่อนจะลงมือทำ operation
127.0.0.1:6379> SET pageviews:home 0OK127.0.0.1:6379> INCR pageviews:home(integer) 1127.0.0.1:6379> INCR pageviews:home(integer) 2127.0.0.1:6379> INCR pageviews:home(integer) 3127.0.0.1:6379> DECR pageviews:home(integer) 2SET pageviews:home 0
INCR pageviews:home
INCR pageviews:home
INCR pageviews:home
DECR pageviews:homeINCRBY และ DECRBY
หัวข้อที่มีชื่อว่า “INCRBY และ DECRBY”เมื่อคุณต้องการบวกหรือลบค่าอื่นที่ไม่ใช่ 1 ให้ใช้ INCRBY และ DECRBY:
127.0.0.1:6379> SET score:player1 100OK127.0.0.1:6379> INCRBY score:player1 50(integer) 150127.0.0.1:6379> DECRBY score:player1 30(integer) 120127.0.0.1:6379> INCRBY score:player1 -10(integer) 110SET score:player1 100
INCRBY score:player1 50
DECRBY score:player1 30
INCRBY score:player1 -10INCRBYFLOAT
หัวข้อที่มีชื่อว่า “INCRBYFLOAT”สำหรับเลขคณิตแบบ floating-point (ราคา, ค่าเฉลี่ย, ข้อมูล sensor) ให้ใช้ INCRBYFLOAT ไม่มีคำสั่ง DECRBYFLOAT — แค่ส่ง delta ที่เป็นค่าลบเข้าไปแทน
127.0.0.1:6379> SET temperature 22.5OK127.0.0.1:6379> INCRBYFLOAT temperature 1.3"23.8"127.0.0.1:6379> INCRBYFLOAT temperature -5.0"18.8"127.0.0.1:6379> GET temperature"18.8"SET temperature 22.5
INCRBYFLOAT temperature 1.3
INCRBYFLOAT temperature -5.0
GET temperatureรูปแบบใช้งานจริง: counter นับ page-view
หัวข้อที่มีชื่อว่า “รูปแบบใช้งานจริง: counter นับ page-view”เพราะ INCR จะตั้งค่า key ที่หายไปเป็น 0 ก่อนเพิ่มค่า คุณจึงไม่จำเป็นต้อง SET 0 ก่อนเลย รวมเข้ากับ EXPIRE เพื่อทำ counter แบบ rolling-window:
127.0.0.1:6379> INCR views:article:42(integer) 1127.0.0.1:6379> INCR views:article:42(integer) 2127.0.0.1:6379> INCR views:article:42(integer) 3127.0.0.1:6379> GET views:article:42"3"INCR views:article:42
INCR views:article:42
INCR views:article:42
GET views:article:42รูปแบบใช้งานจริง: rate limiter
หัวข้อที่มีชื่อว่า “รูปแบบใช้งานจริง: rate limiter”ติดตามว่า IP หนึ่งยิง request มากี่ครั้งภายในหนึ่ง window ใช้ INCR ร่วมกับ EXPIRE ที่ตั้งเฉพาะตอน increment ครั้งแรก:
127.0.0.1:6379> INCR ratelimit:ip:203.0.113.5(integer) 1127.0.0.1:6379> EXPIRE ratelimit:ip:203.0.113.5 60(integer) 1127.0.0.1:6379> INCR ratelimit:ip:203.0.113.5(integer) 2127.0.0.1:6379> TTL ratelimit:ip:203.0.113.5(integer) 57ใน code ของแอปพลิเคชันคุณจะเช็กว่า: ถ้าผลลัพธ์ของ INCR เกิน limit ที่ตั้งไว้ (เช่น 100) ก็ปฏิเสธ request นั้น key จะลบตัวเองอัตโนมัติหลังผ่านไป 60 วินาที เป็นการ reset window ใหม่
INCR ratelimit:ip:203.0.113.5
EXPIRE ratelimit:ip:203.0.113.5 60
INCR ratelimit:ip:203.0.113.5
TTL ratelimit:ip:203.0.113.5ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
ใช้ INCR/INCRBY ทำ counter ใน Redis | atomic โดย default ไม่ต้อง lock ฝั่ง application รองรับ throughput สูง | ค่าอยู่แค่ใน key เดียว ไม่มี history หรือ audit trail แบบที่ table ใน DB ให้ได้ |
ผูก EXPIRE เข้ากับ counter (rate limiter) | ได้ rolling window ฟรี ไม่ต้องมี cleanup job ลบ counter เก่า | ถ้าลืมตั้ง EXPIRE ตอน increment ครั้งแรก counter จะกลายเป็น persistent ค้างอยู่ตลอดไป |
ใช้ INCRBYFLOAT แทน read-modify-write ฝั่ง client | เลี่ยง race condition ได้เหมือน INCR | ไม่มี DECRBYFLOAT ต้องส่งค่าติดลบเอง และ floating-point อาจสะสม rounding error เล็กน้อยเมื่อบวกลบซ้ำหลายรอบ |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- อ่านค่าปัจจุบันด้วย
GETแล้วบวกเองก่อนเขียนกลับด้วยSET— เปิดช่องให้เกิด race condition แบบคลาสสิกเมื่อมีหลาย client เขียนพร้อมกัน ควรใช้INCR/INCRBYเสมอเพื่อความเป็น atomic - ลืมตั้ง
EXPIREให้ counter ที่ควรเป็น rolling window (เช่น rate limiter) — ถ้าไม่ตั้ง TTL ตอน increment ครั้งแรก counter จะไม่มีวันรีเซ็ต และ key จะค้างอยู่ใน memory ตลอดไป - คิดว่า
INCRต้องมี key อยู่ก่อนถึงจะใช้ได้ — จริง ๆ แล้วINCRจะตั้งค่าเริ่มต้นเป็น0ให้อัตโนมัติถ้า key ยังไม่มี ไม่จำเป็นต้องSET key 0ล่วงหน้า
💡 ตัวอย่างจากของจริง
GitHub — ใช้ counter แบบ
INCRเก็บ API rate-limit ต่อ token ผูกกับEXPIREเพื่อ reset window อัตโนมัติโดยไม่ต้องมี cron job มาล้างข้อมูลเก่าReddit/YouTube-style platforms — ใช้
INCRนับ view count และ upvote count แบบ near-real-time แทนการอัปเดตแถวใน relational DB ทุกครั้งที่มีคนดู