ウェブサイト検索

Spring Boot Redisキャッシュ


Spring Boot Redisキャッシュ

この記事では、サンプルのSpring Bootアプリケーションをセットアップし、Redisキャッシュと統合します。Redisはデータベース、キャッシュ、メッセージブローカーとして使用されるオープンソースのメモリ内データ構造ストアですが、今回のレッスンではキャッシングの統合のみを示します。プロジェクトを迅速にセットアップするために、Spring Initializrツールを利用します。

Spring Boot Redisプロジェクトのセットアップ

プロジェクトを迅速にセットアップするために、Spring Initializrツールを活用します。以下に示す3つの依存関係を使用します:

プロジェクトをダウンロードして解凍してください。組み込みデータベースを使用するため、H2データベースの依存関係を使用しました。このデータベースはアプリケーションが停止されるとすべてのデータが失われます。

Spring Boot Redis キャッシュ Maven 依存関係

ツールの設定は既に完了していますが、手動で設定したい場合は、このプロジェクトにMavenビルドシステムを使用しており、以下が私たちが使用した依存関係です:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.9.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <java.version>1.8</java.version>
</properties>
<dependencies>
  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>

  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-test</artifactId>
     <scope>test</scope>
  </dependency>

  <!-- for JPA support -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>

  <!-- for embedded database support -->
  <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
  </dependency>

</dependencies>

<build>
  <plugins>
     <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
     </plugin>
  </plugins>
</build>

Maven CentralからSpring Bootの安定したバージョンを使用してください。

モデルの定義

Redisデータベースにオブジェクトを保存するために、基本的なフィールドを持つPersonモデルオブジェクトを定義します:

package com.journaldev.rediscachedemo;

import javax.persistence.*;
import java.io.Serializable;

@Entity
public class User implements Serializable {

    private static final long serialVersionUID = 7156526077883281623L;

    @Id
    @SequenceGenerator(name = "SEQ_GEN", sequenceName = "SEQ_USER", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_GEN")
    private Long id;
    private String name;
    private long followers;

    public User() {
    }

    public User(String name, long followers) {
        this.name = name;
        this.followers = followers;
    }

    //standard getters and setters

    @Override
    public String toString() {
        return String.format("User{id=%d, name='%s', followers=%d}", id, name, followers);
    }
}

それはゲッターとセッターを持つ標準的なPOJOです。

Redisキャッシュの設定

Spring Bootと必要な依存関係がMavenで既に動作している場合、application.propertiesファイルにわずか3行でローカルのRedisインスタンスを設定できます:

Redis Config
spring.cache.type=redis
spring.redis.host=localhost
spring.redis.port=6379

また、Spring Bootのメインクラスに@EnableCachingアノテーションを使用してください:

package com.journaldev.rediscachedemo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class Application implements CommandLineRunner {

  private final Logger LOG = LoggerFactory.getLogger(getClass());
  private final UserRepository userRepository;

  @Autowired
  public Application(UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Override
  public void run(String... strings) {
    //Populating embedded database here
    LOG.info("Saving users. Current user count is {}.", userRepository.count());
    User shubham = new User("Shubham", 2000);
    User pankaj = new User("Pankaj", 29000);
    User lewis = new User("Lewis", 550);

    userRepository.save(shubham);
    userRepository.save(pankaj);
    userRepository.save(lewis);
    LOG.info("Done saving users. Data: {}.", userRepository.findAll());
  }
}

埋め込みH2データベースにサンプルデータを追加したいため、CommandLineRunnerを追加しました。

リポジトリの定義

Redisの動作を示す前に、JPA関連の機能のためにリポジトリを定義します。

package com.journaldev.rediscachedemo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository { }

現在のところ必要がないので、メソッド呼び出しはありません。

コントローラーの定義

コントローラは、アクションのためにRedisキャッシュが呼び出される場所です。実際、これはキャッシュが直接関連付けられているため、リクエストがキャッシュ済みの結果を待つためにサービスコードに入る必要がないため、最適な場所です。こちらがコントローラスケルトンです:

package com.journaldev.rediscachedemo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;

@RestController
public class UserController {

  private final Logger LOG = LoggerFactory.getLogger(getClass());

  private final UserRepository userRepository;

  @Autowired
  public UserController(UserRepository userRepository) {
    this.userRepository = userRepository;
  }
   ...
}

さて、キャッシュに何かを入れるために、@Cacheable アノテーションを使用します。

@Cacheable(value = "users", key = "#userId", unless = "#result.followers < 12000")
@RequestMapping(value = "/{userId}", method = RequestMethod.GET)
public User getUser(@PathVariable String userId) {
  LOG.info("Getting user with ID {}.", userId);
  return userRepository.findOne(Long.valueOf(userId));
}

上記のマッピングでは、getUserメソッドが「users」という名前のキャッシュに人を格納し、その人を「userId」というキーで識別し、フォロワーが12000人を超えるユーザーのみを保存します。これにより、キャッシュには非常に人気があり、しばしばクエリされるユーザーが格納されることが保証されます。また、API呼び出しにログステートメントを意図的に追加しました。今この時点でPostmanからいくつかのAPIコールを行いましょう。これらは私たちが行ったコールです:

localhost:8090/1
localhost:8090/1
localhost:8090/2
localhost:8090/2

ログに気付くと、これがそれになります。

... : Getting user with ID 1.
... : Getting user with ID 1.
... : Getting user with ID 2.

何か気づきましたか?私たちは4つのAPI呼び出しをしましたが、3つのログステートメントしかありませんでした。これは、ユーザーID 2のユーザーが29000人のフォロワーを持っているため、そのデータがキャッシュされていたからです。つまり、そのAPI呼び出しが行われた際、データはキャッシュから返され、データベース呼び出しは行われませんでした!

キャッシュを更新中

キャッシュ値は、実際のオブジェクトの値が更新されるたびに更新される必要があります。これは、@CachePutアノテーションを使用することで実現できます。

@CachePut(value = "users", key = "#user.id")
@PutMapping("/update")
public User updatePersonByID(@RequestBody User user) {
  userRepository.save(user);
  return user;
}

これにより、個人は再びそのIDによって特定され、結果が更新されます。

キャッシュをクリアする

実際のデータベースからデータが削除される場合、キャッシュに保持する意味がなくなります。@CacheEvict アノテーションを使用してキャッシュデータをクリアできます:

@CacheEvict(value = "users", allEntries=true)
@DeleteMapping("/{id}")
public void deleteUserByID(@PathVariable Long id) {
  LOG.info("deleting person with id {}", id);
  userRepository.delete(id);
}

前回のマッピングでは、キャッシュエントリを追い出しただけで、他には何もしていません。

Spring Boot Redisキャッシュアプリケーションの実行

このアプリは単一のコマンドを使って簡単に実行できます:

mvn spring-boot:run

Redisキャッシュの制限

Redisは非常に高速ですが、64ビットシステムでのデータの保存には制限がありません。ただし、32ビットシステムでは3GBのデータしか保存できません。利用可能なメモリが多いほどヒット率が向上しますが、Redisが過剰なメモリを占有すると、この傾向は止まります。キャッシュサイズがメモリの制限に達すると、古いデータが削除されて新しいデータのためのスペースが作られます。

要約

このレッスンでは、Redis Cacheが高速なデータインタラクションを提供する力と、それをSpring Bootと最小限かつ強力な設定で統合する方法を見てきました。コメントを自由に残してください。

Spring Boot Redisキャッシュプロジェクトをダウンロードする

関連記事: