

บทนำ: ความท้าทายของ Spark Structured Streaming และความจำเป็นของ Cache
ในยุคที่ข้อมูลไหลเข้ามาอย่างต่อเนื่อง (Real-time Data Streaming) Apache Spark Structured Streaming ได้กลายเป็นหนึ่งในเทคโนโลยีหลักที่นักพัฒนาสาย Data Engineering ใช้งานเพื่อประมวลผลข้อมูลแบบ ETL (Extract, Transform, Load) แบบทันที อย่างไรก็ตาม ปัญหาสำคัญที่พบเสมอคือ ประสิทธิภาพของ State Management และ Latency ที่สูงขึ้น เมื่อมีการ Join ข้อมูลจากหลายแหล่งหรือมีการคำนวณแบบ Stateful เช่น การนับจำนวน Session หรือการหา Top-K แบบ Real-time
การ Cache ข้อมูล (Caching) เป็นกลยุทธ์สำคัญที่ช่วยลดภาระการประมวลผลซ้ำ และลดเวลาในการเข้าถึงข้อมูลเดิมที่ถูกเรียกใช้บ่อยๆ แต่การ Cache ใน Spark Structured Streaming ไม่ใช่เรื่องง่าย เพราะข้อมูลมีการเปลี่ยนแปลงตลอดเวลา และต้องคำนึงถึงความสดใหม่ของข้อมูล (Data Freshness) ควบคู่ไปกับประสิทธิภาพ
บทความนี้จะพาคุณไปรู้จักกับกลยุทธ์การ Cache สำหรับ Spark Structured Streaming โดยใช้ Redis เป็น Cache Layer ซึ่งเป็นที่นิยมอย่างมากในปี 2026 เนื่องจาก Redis มีความเร็วสูง รองรับ数据结构ที่หลากหลาย และมีฟีเจอร์ Time-to-Live (TTL) ที่ช่วยจัดการข้อมูลที่หมดอายุได้อัตโนมัติ
1. ทำความเข้าใจ Spark Structured Streaming และ State Management
ก่อนที่จะลงลึกถึง Redis Cache เราต้องเข้าใจก่อนว่า Spark Structured Streaming ทำงานอย่างไรในส่วนของ State
1.1 Stateful Processing ใน Spark Streaming
Stateful Processing หมายถึงการประมวลผลที่ต้องจดจำข้อมูลจาก Batch ก่อนหน้า เช่น การนับจำนวน Event ที่เกิดขึ้นในแต่ละ Key (Word Count) หรือการคำนวณ Rolling Average โดย Spark จะเก็บ State นี้ไว้ใน State Store ซึ่งโดยค่าเริ่มต้นจะใช้ HDFS หรือ RocksDB
ปัญหาที่พบบ่อย:
- State Store Latency: การอ่าน/เขียน State ไปยัง HDFS หรือ RocksDB อาจมี Latency สูง โดยเฉพาะเมื่อมีข้อมูลปริมาณมาก
- State Size ใหญ่: หาก State มีขนาดใหญ่ (เช่น เก็บ Session ของผู้ใช้หลายล้านคน) การจัดการ State จะกลายเป็น Bottleneck
- Cold Start: เมื่อ Application เริ่มต้นใหม่หรือมีการ Failover ระบบต้องโหลด State ทั้งหมดจาก Disk ซึ่งใช้เวลานาน
1.2 ทำไมต้องใช้ Redis สำหรับ Cache Streaming?
Redis เป็น In-Memory Data Structure Store ที่มีความเร็วสูง (Sub-millisecond latency) และมีฟีเจอร์ที่เหมาะสมกับการทำ Cache ใน Streaming:
- Key-Value Store: ง่ายต่อการแมปข้อมูล State
- TTL (Time-to-Live): จัดการข้อมูลเก่าโดยไม่ต้องเขียนโค้ดลบเอง
- Pub/Sub: ใช้แจ้งเตือนเมื่อข้อมูลมีการเปลี่ยนแปลง
- Streams & Data Types: รองรับ List, Set, Sorted Set, Hash ซึ่งเหมาะกับ Use Case ที่ซับซ้อน
ในปี 2026 Redis 7.x และ Redis Stack (RedisJSON, RediSearch) ได้รับความนิยมเพิ่มขึ้น ทำให้สามารถใช้เป็นทั้ง Cache และ State Store หลักได้
2. กลยุทธ์การ Cache สำหรับ Spark Structured Streaming
การออกแบบกลยุทธ์ Cache ที่ดีต้องคำนึงถึง 3 ปัจจัยหลัก: ความสดใหม่ (Freshness), ความเร็ว (Speed), และ ความสม่ำเสมอ (Consistency)
2.1 Cache-Aside Pattern (Lazy Loading)
เป็นกลยุทธ์พื้นฐานที่สุด: เมื่อ Spark ต้องการข้อมูล ระบบจะตรวจสอบ Redis ก่อน ถ้าไม่มี (Cache Miss) ก็จะไปดึงจากแหล่งข้อมูลหลัก (เช่น Database) แล้วนำมาเก็บใน Redis พร้อมกำหนด TTL
ข้อดี: ง่ายต่อการ Implement, ลดภาระ Database
ข้อเสีย: อาจเกิด Cache Stampede เมื่อมีคำขอจำนวนมากพร้อมกัน (Thundering Herd)
2.2 Write-Through Cache
ทุกครั้งที่มีการเขียนข้อมูลลง State Store (หรือแหล่งข้อมูลหลัก) ข้อมูลจะถูกเขียนไปยัง Redis ทันที ทำให้ Cache มีความสดใหม่เสมอ
ข้อดี: ข้อมูลใน Cache สอดคล้องกับข้อมูลจริงตลอดเวลา
ข้อเสีย: เพิ่ม Latency ใน Write Path, เปลืองทรัพยากรหากข้อมูลไม่ถูกอ่านบ่อย
2.3 Time-Based Expiration (TTL) + Refresh
ตั้งค่า TTL สำหรับ Cache Key แต่ละตัว เช่น 5 นาที เมื่อ TTL หมด ข้อมูลจะถูกลบอัตโนมัติ และ Spark จะดึงข้อมูลใหม่จากแหล่งต้นทางครั้งต่อไป
ข้อดี: ลดภาระการจัดการ Cache Staleness
ข้อเสีย: ถ้า TTL นานเกินไป ข้อมูลอาจล้าสมัย
ตารางเปรียบเทียบกลยุทธ์ Cache:
| กลยุทธ์ | ความสดใหม่ | Latency (Read) | Latency (Write) | ความซับซ้อนในการ Implement |
|---|---|---|---|---|
| Cache-Aside | ปานกลาง (ขึ้นกับ TTL) | สูง (กรณี Miss) | ต่ำ | ต่ำ |
| Write-Through | สูงมาก | ต่ำ | สูง | ปานกลาง |
| TTL + Refresh | ปานกลาง-สูง | ต่ำ (กรณี Hit) | ต่ำ | ต่ำ-ปานกลาง |
3. การ Implement Redis Cache กับ Spark Structured Streaming
ในส่วนนี้เราจะลงมือเขียนโค้ดตัวอย่างการเชื่อมต่อ Spark Streaming กับ Redis โดยใช้ Redis Spark Connector (เวอร์ชันล่าสุดในปี 2026) และ Jedis หรือ Lettuce เป็น Client
3.1 การติดตั้ง Dependency
สำหรับ Spark 3.5+ และ Scala 2.13 ให้เพิ่ม Dependency ใน build.sbt หรือ pom.xml ดังนี้:
// build.sbt
libraryDependencies ++= Seq(
"org.apache.spark" %% "spark-sql" % "3.5.1",
"com.redislabs" %% "spark-redis" % "4.0.0",
"redis.clients" % "jedis" % "5.1.0"
)
3.2 การตั้งค่า Spark Session และ Redis Connection
ตัวอย่างการสร้าง Spark Session ที่เชื่อมต่อ Redis:
import org.apache.spark.sql.SparkSession
val spark = SparkSession.builder()
.appName("RedisCacheStreaming")
.master("local[*]")
.config("spark.redis.host", "localhost")
.config("spark.redis.port", "6379")
.config("spark.redis.ttl", "300") // ตั้งค่า TTL ทั่วไป 5 นาที
.getOrCreate()
// สำหรับการเชื่อมต่อ Redis แบบ Cluster
// .config("spark.redis.cluster.nodes", "node1:6379,node2:6379")
3.3 ตัวอย่างการทำ Cache-Aside Pattern สำหรับ Lookup Data
สมมติว่าเรามี Stream ของ User Click Events และต้องการ Join กับ User Profile ที่เก็บใน PostgreSQL โดยใช้ Redis เป็น Cache
import org.apache.spark.sql.functions._
import redis.clients.jedis.Jedis
// ฟังก์ชันสำหรับดึงข้อมูลจาก Redis หรือ Database
def getUserProfile(userId: String): String = {
val jedis = new Jedis("localhost", 6379)
try {
val cached = jedis.get(s"user_profile:$userId")
if (cached != null) {
cached
} else {
// ถ้าไม่มีใน Cache ให้ดึงจาก Database (จำลอง)
val profile = s"Profile of $userId - ${System.currentTimeMillis()}"
jedis.setex(s"user_profile:$userId", 300, profile) // TTL 300 วินาที
profile
}
} finally {
jedis.close()
}
}
// ใช้ UDF (User Defined Function) ใน Spark Streaming
val getUserProfileUDF = udf((userId: String) => getUserProfile(userId))
val clickStream = spark.readStream
.format("kafka")
.option("kafka.bootstrap.servers", "localhost:9092")
.option("subscribe", "user_clicks")
.load()
.selectExpr("CAST(value AS STRING) as json")
.select(from_json(col("json"), schema).as("data"))
.select("data.user_id", "data.timestamp")
val enrichedStream = clickStream.withColumn(
"user_profile",
getUserProfileUDF(col("user_id"))
)
// เขียนผลลัพธ์ไปยัง Console หรือ Sink
val query = enrichedStream.writeStream
.outputMode("append")
.format("console")
.start()
query.awaitTermination()
3.4 การใช้ Redis Streams สำหรับ State Store (Advanced)
ในกรณีที่ต้องการใช้ Redis เป็น State Store แทน RocksDB หรือ HDFS (สำหรับ Workload เฉพาะ) สามารถใช้ Redis Streams หรือ RedisJSON ร่วมกับ Spark’s StateStoreProvider API แต่ต้องระวังเรื่อง Consistency และ Performance
4. Best Practices สำหรับ Redis Cache ใน Streaming
จากการทดลองและใช้งานจริงในโปรเจกต์ Data Pipeline ขนาดใหญ่ในปี 2026 มีแนวทางปฏิบัติที่ดีดังนี้:
4.1 การจัดการ Key และ Namespace
- ใช้ Prefix ที่ชัดเจน เช่น
streaming:user_profile:123เพื่อแยก Cache ของแต่ละ Application - หลีกเลี่ยง Key ที่ยาวเกินไป (มากกว่า 1KB) เพราะจะกิน Memory และทำให้ Latency สูงขึ้น
- ใช้ Hash หรือ JSON แทนการเก็บข้อมูลเป็น String Flat หากมีหลาย Field
4.2 การตั้งค่า TTL ที่เหมาะสม
TTL ควรขึ้นอยู่กับลักษณะของข้อมูล:
- ข้อมูลที่เปลี่ยนแปลงบ่อย (Real-time Sensor) → TTL สั้น (30-60 วินาที)
- ข้อมูลอ้างอิง (Reference Data) → TTL ยาว (1-24 ชั่วโมง)
- ข้อมูลที่ต้องมีความแม่นยำสูง → ใช้ Write-Through หรือไม่ Cache เลย
4.3 การจัดการ Memory และ Eviction Policy
Redis ทำงานในหน่วยความจำ ดังนั้นต้องกำหนด Eviction Policy เมื่อ Memory เต็ม:
- allkeys-lru: ลบ Key ที่ไม่ได้ใช้บ่อยที่สุด (เหมาะสำหรับ Cache)
- volatile-ttl: ลบ Key ที่มี TTL ใกล้หมดก่อน
- noeviction: ไม่ยอมลบ (จะ Return Error เมื่อ Memory เต็ม) – ควรหลีกเลี่ยง
4.4 การใช้ Pipeline และ Batch Operation
เมื่อต้องเขียน/อ่านข้อมูลจำนวนมากจาก Redis ควรใช้ Pipeline หรือ Redis Transactions เพื่อลด Round Trip:
val jedis = new Jedis("localhost", 6379)
val pipeline = jedis.pipelined()
// Batch Write
for (userId <- userIds) {
pipeline.setex(s"user_profile:$userId", 300, profileData)
}
pipeline.sync() // ส่งคำสั่งทั้งหมดครั้งเดียว
4.5 การ Monitoring และ Alerting
ใช้เครื่องมือเช่น RedisInsight, Prometheus + Redis Exporter หรือ Grafana เพื่อติดตาม:
- Cache Hit Ratio (ควรสูงกว่า 80%)
- Memory Usage
- Latency (p99)
- จำนวน Connection ที่ถูกเปิด
5. Real-World Use Cases
ในปี 2026 มีหลายองค์กรที่ใช้ Redis Cache ร่วมกับ Spark Structured Streaming อย่างมีประสิทธิภาพ ต่อไปนี้คือกรณีศึกษาจริง:
5.1 Use Case 1: Real-Time Fraud Detection (ธนาคารชั้นนำ)
ปัญหา: ธนาคารต้องการตรวจจับธุรกรรมที่น่าสงสัยแบบ Real-time โดยต้องตรวจสอบ Blacklist, Transaction Limit, และ Location ของผู้ใช้ภายใน < 100ms
วิธีแก้:
- ใช้ Redis เป็น Cache สำหรับ Blacklist (TTL 1 ชั่วโมง) และ User Profile (TTL 5 นาที)
- ใช้ Redis Streams เก็บ Event Sequence สำหรับ Pattern Matching (เช่น การทำธุรกรรมหลายครั้งในเวลาสั้น)
- Spark Streaming อ่านข้อมูลจาก Kafka → Join กับ Redis Cache → ส่งผลลัพธ์ไปยัง Alert System
ผลลัพธ์: ลด Latency จาก 500ms เหลือ 50ms, Cache Hit Ratio 95%
5.2 Use Case 2: E-Commerce Real-Time Dashboard (แพลตฟอร์มช้อปปิ้ง)
ปัญหา: ต้องการแสดงยอดขาย, สินค้าขายดี, และจำนวนผู้เข้าชมแบบ Real-time โดยข้อมูลต้อง Update ทุก 1-2 วินาที
วิธีแก้:
- ใช้ Redis Sorted Set เก็บ Top-K สินค้าขายดี (ZADD + ZREVRANGE)
- ใช้ Redis Hash เก็บยอดขายรวมของแต่ละหมวดหมู่
- Spark Streaming ทำ Aggregation (Windowed Count) → เขียนผลลัพธ์ไปยัง Redis
- Dashboard อ่านจาก Redis โดยตรง (ไม่ต้อง Query Spark อีก)
ผลลัพธ์: Dashboard Refresh ทุก 1 วินาที, ลด Load บน Spark Driver
5.3 Use Case 3: IoT Sensor Data Enrichment (โรงงานอัจฉริยะ)
ปัญหา: เซนเซอร์หลายพันตัวส่งข้อมูล Temperature, Vibration ทุกวินาที ต้อง Join กับ Metadata ของเซนเซอร์ (ตำแหน่ง, รุ่น, ค่าปกติ)
วิธีแก้:
- Metadata เปลี่ยนแปลงน้อยมาก (ปีละครั้ง) → Cache ด้วย TTL 24 ชั่วโมง
- ใช้ RedisJSON เก็บข้อมูล Metadata แบบ Nested (เช่น ตำแหน่ง, ขีดจำกัด)
- Spark Streaming ใช้ UDF อ่านจาก Redis → Enrich → ส่งไปยัง Anomaly Detection Model
ผลลัพธ์: ลด Database Query จาก 10,000 QPS เหลือ < 100 QPS
6. การเปรียบเทียบ Redis กับ Cache ตัวอื่นในปี 2026
แม้ว่า Redis จะเป็นตัวเลือกยอดนิยม แต่ก็มี Cache Engine อื่นๆ ที่น่าสนใจ:
| คุณสมบัติ | Redis | Memcached | Hazelcast | Apache Ignite |
|---|---|---|---|---|
| Data Structures | หลากหลาย (String, Hash, List, Set, Sorted Set, Stream, JSON) | จำกัดเฉพาะ String | Map, Set, List, Queue | SQL, Key-Value, Compute Grid |
| Persistence | RDB, AOF | ไม่มี (Volatile) | มี (Map Store) | มี (Native Persistence) |
| Pub/Sub | มี (Redis Pub/Sub, Streams) | ไม่มี | มี (Topic) | มี |
| Latency (p99) | < 1ms | < 1ms | < 5ms | < 10ms |
| การ Integrate กับ Spark | ดีมาก (Spark-Redis Connector) | ปานกลาง (ต้องเขียนเอง) | ดี (Hazelcast Jet) | ดี (Ignite Spark Integration) |
| ความซับซ้อนในการดูแล | ปานกลาง | ต่ำ | สูง | สูง |
สรุป: สำหรับการทำ Cache ใน Spark Structured Streaming ที่ต้องการความเร็วสูง, Data Structure ที่ยืดหยุ่น, และความง่ายในการ Integrate Redis ยังคงเป็นตัวเลือกอันดับหนึ่ง ในปี 2026
7. ข้อควรระวังและข้อจำกัด
แม้ว่า Redis จะมีข้อดีมากมาย แต่ก็มีข้อควรระวังที่นักพัฒนาต้องตระหนัก:
- Memory Bound: Redis เก็บข้อมูลใน RAM ทั้งหมด หากมีข้อมูลมากเกินไป อาจต้องเพิ่ม Memory หรือใช้ Redis Cluster
- Data Loss ในกรณี Failover: หากไม่ตั้งค่า Persistence (RDB/AOF) ข้อมูลอาจสูญหายเมื่อ Redis Restart
- Network Overhead: การเชื่อมต่อ Redis ผ่าน Network อาจเพิ่ม Latency หาก Network ไม่ดี ควรใช้ Redis 在同一 Data Center หรือใช้ Local Cache ร่วม
- Cache Invalidation: การจัดการ Cache Staleness ในระบบ Distributed ทำได้ยาก ต้องออกแบบดีๆ
วิธีลดความเสี่ยง:
- ใช้ Redis Sentinel หรือ Redis Cluster เพื่อ High Availability
- ตั้งค่า Persistence (อย่างน้อย AOF ทุก 1 วินาที)
- ใช้ Connection Pooling (เช่น JedisPool) เพื่อลด Overhead
- ทดสอบ Cache Hit Ratio และปรับ TTL ตามพฤติกรรมข้อมูลจริง
8. เครื่องมือและเทรนด์ในปี 2026
ในปี 2026 มีเครื่องมือและแนวโน้มใหม่ๆ ที่เกี่ยวข้องกับ Redis และ Spark Streaming:
- Redis Stack: รวม RedisJSON, RediSearch, RedisTimeSeries เข้ามา ทำให้สามารถใช้ Redis เป็น Data Platform หลักได้
- Spark Connect + Redis: Spark 3.5+ รองรับ Spark Connect ทำให้สามารถส่ง UDF ไปทำงานที่ Remote Cluster และ Cache ผลลัพธ์ใน Redis ได้
- Kubernetes Native: Redis Operator บน Kubernetes (เช่น KubeDB, Redis Enterprise Operator) ทำให้จัดการ Redis Cluster ได้ง่ายขึ้น
- AI/ML Integration: ใช้ Redis เป็น Feature Store สำหรับ Machine Learning Model ที่ทำงานร่วมกับ Spark Streaming (เช่น การทำ Real-time Inference)
Summary
การเลือกใช้กลยุทธ์ Cache ที่เหมาะสมสำหรับ Spark Structured Streaming เป็นปัจจัยสำคัญที่กำหนดประสิทธิภาพของระบบ Data Pipeline แบบ Real-time โดย Redis ได้พิสูจน์ตัวเองแล้วว่าเป็นตัวเลือกที่ยอดเยี่ยมในด้านความเร็ว, ความยืดหยุ่นของ Data Structure, และการ Integrate กับ Spark ผ่าน Connector ที่พัฒนาเต็มที่
จากบทความนี้ คุณได้เรียนรู้:
- ความท้าทายของ State Management ใน Spark Streaming และบทบาทของ Cache
- กลยุทธ์ Cache หลักๆ 3 แบบ (Cache-Aside, Write-Through, TTL + Refresh) และข้อดีข้อเสียของแต่ละแบบ
- วิธีการ Implement Redis Cache ในโค้จริง พร้อมตัวอย่างการทำ Lookup Cache และการใช้ Pipeline
- Best Practices ในการจัดการ Key, TTL, Memory, และ Monitoring
- กรณีศึกษาจริงจาก Fraud Detection, E-Commerce Dashboard, และ IoT
- การเปรียบเทียบ Redis กับ Cache Engine อื่นๆ
ท้ายที่สุดนี้ ขอให้คุณลองนำแนวทางเหล่านี้ไปปรับใช้กับโปรเจกต์ของคุณ การเริ่มต้นด้วยกลยุทธ์ง่ายๆ อย่าง Cache-Aside ก่อน แล้วค่อยปรับแต่ง TTL และโครงสร้างข้อมูลตามผลลัพธ์ที่ได้ จะช่วยให้คุณสร้างระบบ Streaming ที่ทั้งเร็วและเสถียรได้ในปี 2026 นี้
บทความนี้เป็นส่วนหนึ่งของ SiamCafe Blog — แหล่งรวมความรู้ด้านเทคโนโลยีสำหรับนักพัฒนาไทย