mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
4793 文字
13 分
Redis入門
2023-08-08

本文は黒马のredis動画 に基づいて作成されています#

Redisはキー値型のNoSQLデータベースです。ここには2つのキーワードがあります:

  • キー値型
  • NoSQL

そのうちキー値型は、Redisに保存されるデータがすべてkey.valueのペアとして保存され、valueの形式は多岐にわたり、文字列・数値・さらにはJSONにもなり得ます:

Redis入門#

NoSQL#

NoSQLはNot Only SQL(SQLだけではない)と訳されることが多い、あるいはNo SQL(非SQLの)データベースとも呼ばれます。伝統的なリレーショナルデータベースと比較して大きな差異を持つ、特殊なデータベースであり、したがって非リレーショナルデータベースとも呼ばれます。

  • リレーショナルデータベースとの比較

従来のリレーショナルデータベースは構造化データで、各テーブルには厳密な制約情報があります:フィールド名、フィールドのデータ型、フィールド制約などの情報。挿入されるデータはこれらの制約を遵守しなければなりません:

一方、NoSQLはデータベースの形式に厳密な制約を課さず、しばしば形式がゆるく、自由です。キー値型、ドキュメント型、グラフ型などになり得ます。

従来のデータベースの表と表の間には関連が存在することが多く、例えば外部キーなど

一方、非リレーショナルデータベースには関連関係が存在しません。関係を維持するには、アプリケーションのビジネスロジックで行うか、データ間の結合に頼る必要があります

従来のリレーショナルデータベースはSQL文に基づいてクエリを実行し、構文には統一された標準があります;

しかし、さまざまなNoSQLデータベースごとにクエリ構文は大きく異なり、千差万別です。

従来のリレーショナルデータベースはACIDの原則を満たすことができます

一方、非リレーショナルデータベースは、トランザクションをサポートしない、あるいはACID特性を厳密に保証できないことが多く、基本的な一貫性のみを実現します

kZP40dQ.png

  • 保存方式
    • リレーショナルデータベースはディスク上に保存するため、多くのディスクI/Oが発生し、パフォーマンスに一定の影響を与えます
    • 非リレーショナルデータベースは、操作の多くをメモリ上で行うことが多く、メモリの読み書き速度が非常に速いため、性能は自然と良くなります
  • 拡張性
    • リレーショナルデータベースのクラスタリングは一般にマスター-スレーブ構成で、データは一致し、データバックアップの役割を果たします。垂直拡張と呼ばれます。
    • NoSQLデータベースはデータを分割して異なるマシンに保存することができ、膨大なデータを保存でき、メモリ容量の制限を解決します。水平拡張と呼ばれます。
    • テーブル間に関連があるため、水平拡張を行うとデータのクエリに多くの手間が発生します

Redis#

Redisの正式名称は、Remote Dictionary Server(リモート辞書サーバー)で、メモリ上に基づくキー値型NoSQLデータベースです。

特徴

  • キー値型(key-value)で、valueは多様なデータ構造をサポートし、機能が豊富
  • シングルスレッド、各コマンドは原子性を備えます
  • 低遅延、高速(メモリ上に基づく、I/O多重化。良好なエンコード。)
  • データの永続化をサポート
  • マスター-スレーブ構成とシャーディングをサポート
  • 複数言語のクライアントをサポート

Redis安装#

Dockerでのインストール

docker search redis
docker pul redis
docker run --restart=always -p 6379:6379 --name myredis -v /home/redis/myredis/myredis.conf:/etc/redis/redis.conf -v /home/redis/myredis/data:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes
docker exec -it <容器名> /bin/bash

Redisの一般コマンド#

Redisデータ構造の紹介#

Redisはキー-valueデータベースで、キーは一般的にString型ですが、valueの型は多様です:

image-20230806154902437.png

Redisの共通コマンド#

共通コマンドは、いくつかのデータ型で使用できるコマンドで、よく使われるものは以下のとおりです:

  • KEYS:テンプレートに一致するすべてのキーを表示します
127.0.0.1:6379> keys *
# テンプレート"a"で始まるキーを検索
127.0.0.1:6379> keys a*
1) "age"

本番環境では、keys コマンドの使用は推奨されません。なぜなら、キーが多数ある場合、効率が低いからです

  • DEL:指定したキーを削除します
127.0.0.1:6379> del name #削除単一
(integer) 1 #成功 delete 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3 #複数データを一括追加
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
4) "age"
127.0.0.1:6379> del k1 k2 k3 k4
(integer) 3 #この時点で削除されたキーは3つ。Redisにはk1,k2,k3しかないため、実際には3つが削除され、最終的に3を返します
127.0.0.1:6379> keys * #すべてのキーを再度検索
1) "age" # 残っているのは1つだけ
  • EXISTS:キーが存在するかを判定
127.0.0.1:6379> exists age
(integer) 1
127.0.0.1:6379> exists name
(integer) 0
  • EXPIRE:キーに有効期限を設定。期限が切れると自動的に削除される
  • TTL:キーの残り有効期限を表示
127.0.0.1:6379> expire age 10
(integer) 1
127.0.0.1:6379> ttl age
(integer) 8
127.0.0.1:6379> ttl age
(integer) -2
127.0.0.1:6379> ttl age
(integer) -2 # このキーが期限切れの場合、ttlは-2になる
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set age 10 # 有効期限を設定していない場合
OK
127.0.0.1:6379> ttl age
(integer) -1 # ttlの返り値は-1

Redis命令-String命令#

String型は、Redisで最も基本的なストレージ型です。

その値は文字列ですが、文字列の形式に応じて3つのカテゴリに分けられます:

  • string:通常の文字列
  • int:整数型、インクリメント・デクリメントが可能
  • float:浮動小数点型、インクリメント・デクリメントが可能

Stringの一般的なコマンドは以下のとおり:

  • SET:新規作成または既存のStringキーの値を変更します
  • GET:キーに応じてString型の値を取得します
  • MSET:複数のString型キーと値を一括して追加します
  • MGET:複数のキーに対して複数のString値を取得します
  • INCR:整数型キーを1ずつ増加させます
  • INCRBY:整数型キーを指定したステップ分だけ増加させます。例:INCRBY num 2 で num を2増やします
  • INCRBYFLOAT:浮動小数点数を指定したステップ分だけ増加させます
  • SETNX:キーが存在しない場合のみString型のキー値ペアを追加します。存在する場合は実行されません
  • SETEX:String型のキーと値を追加し、有効期限を指定します

Redis命令-Keyの階層構造#

RedisにはMySQLのようなTableの概念はありません。異なるタイプのキーをどう区別しますか?

Redisのキーは複数の単語で階層構造を形成することが許され、複数の単語の間は’:‘で区切ります

この形式は固定ではなく、用途に応じて語を削除したり追加したりできます。

例えばプロジェクト名をheimaとし、userとproductの2種類のデータがある場合、次のようにキーを定義できます:

  • user関連のキー:heima:user<1>
  • product関連のキー:heima:product<1>

ValueがJavaオブジェクト(例:Userオブジェクト)である場合、オブジェクトをJSON文字列にシリアライズして保存できます:

KEYVALUE
heima:user<1>{“id”<1>, “name”: “Jack”, “age”: 21}
heima:product<1>{“id”<1>, “name”: “小米11”, “price”: 4999}

このようにRedisに保存すると、可視化インターフェース上で階層構造として保存され、Redisのデータ取得がより便利になります

Redis命令-Hash命令#

Hash型、別名ハッシュ、valueは無秩序な辞書で、JavaのHashMap構造に似ています。

String構造はオブジェクトをJSON文字列にシリアライズして保存します。オブジェクトの特定のフィールドを変更する必要がある場合は非常に不便です:

Hash構造はオブジェクトの各フィールドを個別に保存することができ、単一フィールドに対してCRUDを行えます:

Hash型の一般的コマンド

  • HSET key field value:Hash型のkeyのfieldの値を追加または変更します
  • HGET key field:Hash型のkeyのfieldの値を取得します
  • HMSET:複数のhash型keyのfieldの値を一括で追加します
  • HMGET:複数のhash型keyのfieldの値を一括取得します
  • HGETALL:Hash型のkeyに含まれるすべてのfieldとvalueを取得します
  • HKEYS:Hash型のkeyに含まれるすべてのfieldを取得します
  • HINCRBY
  • HSETNX:Hash型のkeyのfield値を追加します。前提としてそのfieldが存在しない場合のみ実行され、存在する場合は実行されません

Redis命令-List命令#

RedisのList型はJavaのLinkedListに似ており、双方向リスト構造と見なすことができます。前方検索も後方検索も可能です。

特徴もLinkedListと似ています:

  • 有序
  • 要素は重複可能
  • 挿入と削除が速い
  • 検索速度は普通

しばしば有序データを保存するのに用いられます。例えば、友だちのタイムラインでのいいねリスト、コメントリストなど。

Listの一般的コマンドは以下のとおり:

  • LPUSH key element … :リストの左側に1つ以上の要素を挿入します
  • LPOP key:リストの左端の最初の要素を削除して返します。なければnilを返します
  • RPUSH key element … :リストの右側に1つ以上の要素を挿入します
  • RPOP key:リストの右端の最初の要素を削除して返します
  • LRANGE key start end:一定範囲のすべての要素を返します
  • BLPOPおよびBRPOP:LPOPおよびRPOPと似ていますが、要素がない場合は指定した時間待機し、nilをすぐには返しません

Redis命令-Set命令#

RedisのSet型はJavaのHashSetに似ており、値はnullのHashMapと見なすことができます。ハッシュテーブルでもあるため、HashSetと同様の特徴を持ちます:

  • 無秩序
  • 要素は重複不可
  • 検索が速い
  • 交差、和集合、差集合などの機能をサポート

Set型の一般的コマンド

  • SADD key member …:Setに1つ以上の要素を追加します
  • SREM key member …:Setから指定した要素を削除します
  • SCARD key:Setの要素数を返します
  • SISMEMBER key member:要素がSetに存在するかを判定します
  • SMEMBERS:Setの全要素を取得します
  • SINTER key1 key2 … :key1とkey2の交差を求めます
  • SDIFF key1 key2 … :key1とkey2の差集合を求めます
  • SUNION key1 key2 ..:key1とkey2の和集合を求めます

Redis命令-SortedSetタイプ#

RedisのSortedSetはソート可能な集合で、JavaのTreeSetに似ていますが、内部データ構造は大きく異なります。SortedSetの各要素はscore属性を持ち、それに基づいてソートされ、内部実装はSkip Listとハッシュテーブルの組み合わせです。

SortedSetの特徴は以下のとおりです:

  • ソート可能
  • 要素は重複しません
  • 検索速度が速い

SortedSetのソート可能という特性のため、ランキングのような機能の実装によく用いられます。

SortedSetの一般的コマンドは以下のとおりです:

  • ZADD key score member:1つ以上の要素をSorted Setに追加します。すでに存在する場合はscore値を更新します
  • ZREM key member:Sorted Setから指定された要素を削除します
  • ZSCORE key member:Sorted Set内の指定要素のscore値を取得します
  • ZRANK key member:指定要素の順位を取得します
  • ZCARD key:Sorted Setの要素数を取得します
  • ZCOUNT key min max:指定範囲内の要素数を統計します
  • ZINCRBY key increment member:指定要素をincrement値だけ自動増加させます
  • ZRANGE key min max:scoreでソートした後、指定した順位範囲の要素を取得します
  • ZRANGEBYSCORE key min max:scoreでソートした後、指定したscore範囲の要素を取得します
  • ZDIFF.ZINTER.ZUNION:差集合・交集合・和集合を求めます

注意:すべての順位はデフォルトで昇順です。降順にしたい場合は、コマンドのZの後ろにREVを追加します。例えば:

  • 昇順でSorted Setの指定要素の順位を取得:ZRANK key member
  • 降順でSorted Setの指定要素の順位を取得:ZREVRANK key member

Javaクライアント-Jedis#

https://redis.io/docs/clients/

  • JedisとLettuce:この2つはRedisコマンドに対応するAPIを提供しており、Redisの操作を容易にします。Spring Data Redisはこれら2つを抽象化・ラップしているため、今後はSpring Data Redisを使って学習します。
  • Redisson:Redisをベースに、Map・Queueなどの分散スケーラブルなJavaデータ構造を実装し、プロセス間の同期機構(Lock・Semaphoreの待機など)をサポートします。特定の機能要件の実現に適しています。

Jedis入門#

依存関係:

<!--jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>

テスト:

private Jedis jedis;
@BeforeEach
void setup() {
// 1.接続を確立
// jedis = new Jedis("192.168.150.101", 6379);
// jedis = JedisConnectionFactory.getJedis();
jedis = new Jedis("127.0.0.1",6379);
// 2.パスワード設定
// jedis.auth("123321");
// 3.データベースを選択
jedis.select(0);
}
@Test
void testString() {
// データを格納
String result = jedis.set("name", "虎哥");
System.out.println("result = " + result);
// データを取得
String name = jedis.get("name");
System.out.println("name = " + name);
}
@Test
void testHash() {
// hashデータを挿入
jedis.hset("user:1", "name", "Jack");
jedis.hset("user:1", "age", "21");
// 取得
Map<String, String> map = jedis.hgetAll("user:1");
System.out.println(map);
}
@AfterEach
void tearDown() {
if (jedis != null) {
jedis.close();
}
}

Jedis连接池#

Jedis自体はスレッドセーフではなく、頻繁な接続の作成と破棄はパフォーマンス損耗を招くため、Jedis直結方式の代わりにJedis接続プールを使用することを推奨します

プール化の思想はここだけでなく、さまざまな場面で見られます。たとえばデータベース接続プール、Tomcatのスレッドプールなど、これらはすべてプール化思想の現れです。

public class JedisConnectionFacotry {
private static final JedisPool jedisPool;
static {
//接続プールを設定
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);
poolConfig.setMaxIdle(8);
poolConfig.setMinIdle(0);
poolConfig.setMaxWaitMillis(1000);
//接続プールオブジェクトを作成
jedisPool = new JedisPool(poolConfig,"127.0.0.1",6379,1000);
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
}

JedisFactoryからJedis接続を取り出す:

@BeforeEach
void setup() {
// 接続を確立
jedis = JedisConnectionFacotry.getJedis();
// データベースを選択
jedis.select(0);
}

SpringDataRedis#

Spring DataはSpringのデータ操作モジュールで、さまざまなデータベースの統合を含み、Redisの統合モジュールはSpring Data Redisと呼ばれます。公式サイトのアドレス:https://spring.io/projects/spring-data-redis

  • LettuceとJedisという異なるRedisクライアントの統合を提供します
  • RedisTemplateを提供し、Redisを操作する統一APIを提供します
  • RedisのPub/Subモデルをサポート
  • Redis SentinelとRedisクラスタをサポート
  • Lettuceベースのリアクティブプログラミングをサポート
  • JDK、JSON、文字列、Springオブジェクトのデータのシリアライズ・デシリアライズをサポート
  • RedisベースのJDKコレクション実装をサポート

SpringDataRedisにはRedisTemplateツールクラスが提供されており、さまざまなRedis操作を包んでいます。また、異なるデータ型の操作APIを異なるタイプに封装しています:

image-20230806163312657.png

SpringDataRedis入門#

pom依存関係:

<!--redis依存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--common-pool-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--Jackson依存-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

yml配置:

spring:
redis:
host: 127.0.0.1
port: 6379
# password: 123321
lettuce:
pool:
max-active: 8 #最大接続
max-idle: 8 #最大空闲接続
min-idle: 0 #最小空闲接続
max-wait: 100ms #接続待機時間

テスト:

@SpringBootTest
class JedisDemoApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void testString(){
redisTemplate.opsForValue().set("name","hg");
Object name = redisTemplate.opsForValue().get("name");
System.out.println(name);
}
}
  • spring-boot-starter-data-redis依存関係を追加
  • application.ymlにRedis情報を設定
  • RedisTemplateを注入

データのシリアライズ#

RedisTemplateは任意のObjectを値としてRedisに書き込むことができます:

image-20230806164703100.png

ただし書き込む前にObjectをバイト列にシリアライズします。デフォルトではJDKシリアライズを使用し、得られる結果はこのようになります:

image-20230806164710423.png

欠点:

  • 読みやすさが悪い
  • メモリ使用量が大きい

カスタムシリアライズ方式

@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory){
// RedisTemplateオブジェクトを作成
RedisTemplate template = new RedisTemplate();
// 接続ファクトリを設定
template.setConnectionFactory(connectionFactory);
// JSONシリアライズツールを作成
GenericJackson2JsonRedisSerializer jsonRedisSerializer =
new GenericJackson2JsonRedisSerializer();
// Keyのシリアライズを設定
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// Valueのシリアライズを設定
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
// 返す
return template;
}
}

StringRedisTemplate#

デシリアライズ時にオブジェクトの型を知るため、JSONシリアライザはクラスの型情報をJSON結果に書き込み、Redisに格納します。これにより余分なメモリ使用量が発生します。

メモリの消費を抑えるため、手動シリアライズの方法を採用します。言い換えれば、デフォルトのシリアライザを使わず、自分でシリアライズの動作を制御します。同時に、文字列シリアライザだけを使用します。これにより、valueを保存する際に、メモリ上で余分にデータを保持する必要がなくなり、メモリ空間を節約できます

@Autowired
private StringRedisTemplate stringRedisTemplate;
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testSaveUser() throws JsonProcessingException {
// オブジェクトを作成
User user = new User("hg", 21);
// 手動シリアライズ
String json = mapper.writeValueAsString(user);
// データを書き込む
stringRedisTemplate.opsForValue().set("user:200", json);
// データを取得
String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
// 手動デシリアライズ
User user1 = mapper.readValue(jsonUser, User.class);
System.out.println("user1 = " + user1);
}

RedisTemplateの2つのシリアライズ実践案:

  • 案1:
    • 自作RedisTemplate
    • RedisTemplateのシリアライザをGenericJackson2JsonRedisSerializerへ変更
  • 案2:
    • StringRedisTemplateを使用
    • Redisへ書き込む際、オブジェクトを手動でJSONにシリアライズ
    • Redisから読み取る際、読み取ったJSONを手動でデシリアライズしてオブジェクト化
共有

この記事が役に立ったときは、ぜひ他の人に共有してください!

Redis入門
https://dreaife.tokyo/jp/posts/redis-learn-basics/
著者
dreaife
公開日
2023-08-08
ライセンス
CC BY-NC-SA 4.0

一部の情報は古い可能性があります

関連した投稿 スマート
1
Redis実戦:ECサイトシステム
middle-side 本記事では、SMSログイン、店舗検索キャッシュ、クーポン秒殺、ユーザーのチェックイン、UV統計などの機能を含む、RedisベースのECサイトシステム実装を紹介します。Redisを用いて高並行処理を実現し、キャッシュ貫通・キャッシュ雪崩・キャッシュブレークダウンの問題を解決し、分散ロックやメッセージキューで性能を最適化します。実装では、GEO、BitMap、HyperLogLogなどRedisの多様なデータ構造と操作を利用します。
2
RabbitMQ入門
middle-side RabbitMQは同期通信と非同期通信をサポートするメッセージキューです。非同期通信では仲介役のBrokerを介して疎結合化し、スループット向上と障害分離を実現します。RabbitMQはDockerで導入でき、ワークキュー、Publish/Subscribe、ルーティングなど複数のメッセージモデルをサポートします。Spring AMQPはキューの自動宣言や非同期受信機能を提供し、RabbitMQ利用を簡素化します。メッセージコンバータにはJSON形式を使うことで可読性と効率を高められます。
3
Elasticsearch入門
middle-side ElasticsearchはLuceneを基盤とした強力なオープンソース検索エンジンで、データ保存、検索、分析によく利用されます。中核概念には、転置インデックス、ドキュメントとフィールド、インデックスとマッピングが含まれます。MySQLとの比較では、データ処理におけるそれぞれの強みの違いが示されます。導入と利用では、インデックス作成、ドキュメント操作、REST APIによる検索を扱います。集計機能は統計分析を可能にし、自動補完やデータ同期はユーザー体験と整合性を向上させます。クラスタ管理は高可用性とデータ安全性を支えます。
4
Spring Cloud入門
middle-side マイクロサービスアーキテクチャは、システムを独立したサービスに分割することで結合度を下げ、柔軟性を高めます。Spring Cloudは、サービス登録、リモート呼び出し、監視、設定管理などを統合した人気のマイクロサービスフレームワークです。EurekaとNacosは主要なサービスレジストリで、サービスディスカバリと負荷分散を提供します。Feignはリモート呼び出し実装を簡素化し、Spring Cloud Gatewayは権限管理やレート制限を備えた統一的なAPIルーティング管理を提供します。設定管理やCORS問題への対応策についても詳しく説明しています。
5
Java NIO
cs-base NIO(New I/O)は、従来のBIOの性能ボトルネックを解消するためにJava 1.4で導入されたノンブロッキングI/Oモデルです。中核コンポーネントにはBuffer、Channel、Selectorがあり、少数のスレッドで複数接続を処理できます。NIOはゼロコピー技術もサポートしてI/O効率を高め、ネットワークプログラミング簡略化のためにNettyのようなNIOベースのフレームワーク利用が推奨されます。

目次