ทำ Caching ด้วย Spring + Redis Cache

Phayao Boonon
4 min readDec 30, 2018

การเรียกใช้ข้อมูลจากฐานข้อมูล (database) ในแต่ละครั้งนั้นจะใช้เวลาพอสมควร ถ้าเป็นระบบที่มีการเรียกใช้งานจำนวนมากๆ จะทำให้ load ของฐานข้อมูลมีจำนวนมหาศาล ดังนั้นการ Caching สามารถช่วนแบ่งเบาภาระสำหรับการดำเนินงานกันฐานข้อมูล ในกรณีที่มีข้อมูลในที่เป็นข้อมูลซ้ำๆ เดิม การอ่านข้อมูลจาก Cache จะเร็วกว่าการอ่านข้อมูลโดยตรงจากฐานข้อมูลมาก ซึ่งบทความนี้จะเสนอการจัดการ Cach ด้วย Spring Framework ซึ่งรองรับการ Caching และเก็บข้อมูลไว้บน Redis

Caching?

การ Cache เป็นกระบวนการสำหรับเก็บข้อมูลใน Cache ซึ่ง Cache เป็นที่เก็บข้อมูลชั่วคราวที่ซึ่งเก็บข้อมูลไว้ใช้ในภายหลัง ง่ายต่อการเข้าถึงทั้ง Client หรือ Server ตรงกันข้ามกับที่เก็บข้อมูลถาวรที่อาจจะเก็บไว้ใน Server ที่แตกต่างกันและใช้เวลาในการเข้าถึงที่นานกว่า (Database หรือ API ภายนอก)

https://www.codementor.io/brainyfarm/introduction-to-caching-and-redis-h6o16p4qx

Spring Cache

เป็น abstract class ของ Spring Framework ที่รองรับการเพิ่มการ caching เข้าไปใน Spring application ซึ่ง Caching abstraction ทำให้ใช้งาน caching solution ที่หลากหลาย ด้วยการเปลี่ยนแปลงโค้ดที่น้อยที่สุด

เพื่อใช้งาน Spring Caching Abstraction จะมี 2 สิ่งที่ต้องทำคือ

  • Caching Declaration — เป็นการระบุ method สำหรับการ caching และ policy ของมัน
  • Cache Configuration — เป็นการระบุระบบเบื้องหลัง cache ว่าจะเก็บข้อมูลไว้ที่ไหน หรือจะไปอ่านข้อมูลจากไหน

ซึ่งจะมี annotation สำหรับการ Caching ดังนี้

  • @Cacheable — กระตุ้นการเรียกข้อมูลของ cache
  • @CachEvict — ทำลายการ cache
  • @CachePut — เปลี่ยนแปลงข้อมูลของ cache
  • @Caching — เป็นการจัดกลุ่มหลายๆ cache operation เข้าด้วยกัน
  • @CachConfig — ตั้งค่า config ของการ cache ในระดับ class

Create Spring Boot Project

สร้าง Spring Boot project ด้วย Spring Initializr เป็น Web, Cache, Redis, H2, JPA และ Lombok

ใน pom.xml จะสังเกตุเห็นว่ามี dependency spring-boot-starter-cache สำหรับ Cache และ spring-boot-starter-data-redis สำหรับ Redis

ใช้ h2database เพื่อเป็น In-Memory database และ spring-boot-starter-data-jpa สำหรับ Spring Data JPA ใช้ในการติดต่อกับ H2 database

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
...
</dependencies>

เพิ่ม application configuration ให้กับ project เพื่อตั้งค่าให้กับ Spring Cache เป็น redis และตั้งค่าเวลาอายุของ cache เป็น 60000 วินาที และตั้งค่า host และ port ที่เชื่อมต่อกับ Redis เป็น localhost และ 6379

spring.cache.type=redis
spring.cache.redis.time-to-live
=60000
spring.redis.host
=localhost
spring.redis.port
=6379

Create RESTful Service

สร้าง RESTful Service สำหรับ CRUD operation สำหรับ User ง่ายๆ /user

Model [User]

Controller [UserController]

Service [UserService]

Repository [UserRepository]

Add Cache Abstraction

ใช้ @EnableCaching annotation เพื่อเปิดใช้งาน Spring Cache

เพิ่ม Cache Abstraction ให้กับ UserService เพราะว่าเราจะทำการ cache ใน Service level

ใช้ @Cacheable annotation สำหรับ retrieveUserById method ที่ query user data ด้วย User ID โดย parameter value เป็นชื่อของ cache กำหนดเป็น “user”, key เป็น key ของ cache กำหนดเป็น “#id” และ unless เป็น parameter สำหรับกำหนดเงื่อนไขว่าผลลัพธ์ของ cache โดยกำหนดให้ “#result==null” ซึ่งเป็นการเพิ่ม User data ให้กับ Cache data store

ใช้ @CachPut annotation สำหรับ updateUserById method ที่ update user data ด้วย User ID และ User Data โดยกำหนด parameter ให้เหมือนกับ @Cacheable ซึ่งเป็นการแก้ไขข้อมูลของ User data ที่อยู่ใน Cache data store

ใช้ @CacheEvict annotation สำหรับ deleteUser method ที่ delete user data ด้วย User ID โดยกำหนด parameter value เป็น “user” ซึ่งเป็นการลบ User data ใน Cache data store

Run Redis

เริ่มต้นการทำงานของ Redis server ด้วยคำสั่ง redis-server

Run Spring Boot App

เริ่มต้นการทำงาน Spring Boot Application ด้วยคำสั่ง mvn spring-boot:run

Test Application

ใช้ Postman มาทดสอบว่า Spring Boot App ที่เราสร้างขึ้นมีการ cache หรือไม่ โดยเริ่มแรกสร้าง User data ก่อนด้วย POST method และใช้ GET method get data ด้วย User ID ที่สร้างขึ้น ซึ่งในที่นี้คือ User ID 1

POST /users

GET /user/1

สังเกตุใน application console จะเห็นว่ามีข้อความ “Retrieve User ID: 1” ซึ่งเป็นข้อความจาก retrieveUserById method เมือเรา GET request อีกครั้งจะเห็นได้ว่ามี response กลับมาจาก request แต่ว่าใน application console ไม่มีข้อความเพิ่มขึ้นอีก แสดงว่า User data กูกเก็บไว้ใน cache เรียบร้อยแล้ว

เมื่อใช้ redis-cli ดู key ที่อยู่ใน Redis จะเห็นว่ามี key user::1 อยู่ ถ้าลอง delete key ด้วยคำสั่ง FLUSHALL

ใช้ GET request อีกครั้งจะเห็นว่ามีข้อความ “Retrieve User ID: 1” เพิ่มขึ้นเพราะว่ามีการไป query User data จาก database อีกครั้งและเก็บไว้ใน cache อีกครั้ง

สรุป

จากที่ได้ลองทำ Cache ของ RESTful web service ที่เก็บข้อมูลใน RDMS database ด้วย Spring + Redis Cache แล้วจะเห็นได้ว่า Spring framework รองรับการทำ Caching ด้วย data store ที่เป็น In-Memory NoSQL database อย่าง Redis ได้เป็นอย่างดีและการ configuration ต่างๆ เบื้องต้นก็ทำได้ไม่ยาก ทำให้เราลด database operation ลงได้มากถ้า web service บริการ request จำนวนมากด้วยข้อมูลเดิม

--

--

Phayao Boonon

Software Engineer 👨🏻‍💻 Stay Hungry Stay Foolish