Redis の基本的な使い方と高度な知識
Redis は、キーと値のペア key と value を使用してデータを保存する非リレーショナル データベースです。そのデータはメモリに保存されるため、読み取りと書き込みの効率が高くなります。一般的に使用されるシナリオには、次のようなものがあります: 大量のデータの読み取りと書き込み、大量のデータ同時読み取りと書き込み、データのキャッシュなど。
1. 基本的なデータ型
Redis データベースには 5 つの基本データ型があり、Redis の新しいバージョンでは 3 つの新しいデータ型 、 、 が追加されています。
Bitmaps
これらHyperLogLog
についてGeospatial
はここでは説明しませんが、5 つの基本データ型について簡単に説明するだけです。
1.String
字符串类型,存储的 value 数据为字符串。
2.List
列表类型,存储的 value 数据为一个列表,有序列表,其数据排列的顺序与数据添加的顺序相同。
3.Hash
哈希类型,存储的 value 数据为一个对象,对象内也是采用 key 和 value 进行存储数据的,类似于Java中的 map 数据类型或者Python中的字典 dict 类型。
4.Set
集合类型,存储的 value 数据为一个集合,该集合是 无序 的,集合中不允许有重复值,会自动去重。
5.Zset
有序集合类型,存储的 value 数据也是集合,该集合是 `有序` 的,通过给每个 value 设置 score 值,按照每个 value 的 score 值进行排序,同时该集合也是不包含重复值的,自动去重。
2. 基本的な操作コマンド
1.String
String 文字列型での一般的なコマンド操作
# 设置key为k1,值为10
set k1 10
# 获取key为k1的值
get k1
# 对key为k1的value值进行+1
incr k1
# 对key为k1的value值进行+20
incrby k1 20
# 对key为k1的value值进行-1
decr k1
# 对key为k1的value值进行-20
decrby k1 20
# 设置k1的过期时间为20s
expire k1 20
# 设置k2的值为v2,过期时间为30s
setex k2 30 v2
# 查看k2的过期时间(-1表示永久有效,-2表示已失效,其他数字表示剩余过期时间)
ttl k2
# 设置k2的值为v2,若k2已存在,则不设置,否则设置。
setnx k2 v2
# 同时设置多个key和value
mset k1 v1 k2 v2
# 同时获取多个key的value
mget k1 k2
2.List
List リスト型での共通コマンド操作
# 从左边依次添加数据至t1列表中,结果为 3 2 1
lpush t1 1 2 3
# 从右边依次添加数据至t2列表中,结果为 1 2 3
rpush t2 1 2 3
# 从t1列表中获取索引从0到-1的所有value值,索引0代表第一个,索引-1代表最后一个
lrange t1 0 -1
# 从左边依次删除t1列表中的值
lpop t1
# 从右边依次删除t1列表中的值
rpop t1
# 获取t1列表的长度
llen t1
3.Hash
ハッシュ型での一般的なコマンド操作
# 设置key为k1中的id值等于1001,一次只能设置一个
hset k1 id 1001
# 设置key为k1中的name=lucy,age=20,一次可以设置多个值
hmset k1 name lucy age 20
# 获取k1中id的值
hget k1 id
# 一次性获取k1中的id、name、age值
hmget k1 id name age
# 查看key为k1中的所有属性key 结果-> id name age
hkeys k1
# 查看key为k1中的所有属性key所对应的值 结果-> 1001 lucy 20
hvals k1
# 对key为k1中的age值+2
hincrby k1 age 2
4.Set
Set コレクション タイプでの一般的なコマンド操作
# 添加 1 2 3 元素到t1集合中
sadd t1 1 2 3
# 删除t1集合中的3元素
srem t1 3
# 查看t1集合中是否包含4这个元素
sismember t1 4
# 查看t1集合中的所有元素
smembers t1
5.Zset
Zset 順序付きコレクション型での一般的なコマンド操作
# 添加 java c++ php 到t1集合中,并指定相应的score值为 100 200 300
zadd t1 100 java 200 c++ 300 php
# 删除t1集合中的java元素
zrem t1 java
# 查看t1集合中索引从0到-1的所有元素(索引0代表第一个元素,索引-1代表最后一个元素)
# 结果 "java" "c++" "php" (按照score值进行从小到大排序)
zrange t1 0 -1
# 查看t1集合中索引从0到-1的所有元素,并显示相应的score值。
# 结果 "java" 100 "c++" 200 "php" 300
zrange t1 0 -1 withscores
# 查看t1集合中索引从0到-1的所有元素,并按倒序(从大到小)排序
# 结果 "php" "c++" "java"
zrevrange t1 0 -1
# 查看t1集合中索引从0到-1的所有元素,按倒序(从大到小)排序,并显示相应的score值。
# 结果 "php" 300 "c++" 200 "java" 100
zrevrange t1 0 -1 withscores
他の一般的なコマンドを補足する
# 查看符合正则表达式的所有key -> (keys pattern)
# 查看所有的key
keys *
# 删除对应的key
del key
# 设置key的过期时间为20s
expire key 20
# 查看key的过期时间(-1表示永久有效,-2表示已失效,其他数字表示剩余过期时间)
ttl key
3. 高度な知識
1. データの永続性
Redis のデータはメモリに書き込まれるため永続化されず、サーバーを再起動するとデータが失われるため、データを永続化してディスクに書き込む必要があります。
-
RDB模式
: ユーザーが設定した時間間隔に従って、条件を満たすデータをバックアップ用ディスクに書き込みます。長所と短所: 効率は高いですが、最後の時間間隔のデータが失われる可能性があります。
-
AOF模式
: すべての書き込み操作をログ ファイルに記録し、サーバーの再起動時にログ ファイルに従ってデータを復元します。長所と短所: データの信頼性を完全に保証できますが、データの回復が比較的遅くなります。
2. マスター/スレーブ レプリケーション
1 つのサーバーはマスター サーバーとして機能し、書き込み操作のみを担当し、他のサーバーはマスター サーバーのスレーブ サーバーとして機能し、データの読み取りのみを担当します。
利点: 読み取りと書き込みの分離、災害復旧、高速復旧
欠点: レプリケーション遅延 (スレーブ同期データには一定の遅延があります)
-
一仆二主
: 1 つのマスターが複数のスレーブに接続されています -
薪火相传
: マスターとスレーブが接続され、スレーブの配下にスレーブが接続されます。 -
反客为主
: マスター マシンがダウンすると、コマンドを入力することでスレーブ マシンの 1 台がマスター マシンにslaveof no one
アップグレードします(センチネル モードを使用して自動的にアップグレードすることもできます)アクションが完了しました)
-
哨兵模式
: マスターの実行ステータスを監視し、マスターがダウンしている場合は、スレーブの 1 つをマスターにアップグレードします。
3. クラスター
クラスタはサービスの高可用性を確保するためのもので、一部のサーバーがダウンしてもサービスを継続して提供できます。
アドバンテージ:
1.容量不足の問題を解決できます。
2. 能够解决高并发读写的问题,分摊服务器压力。 2. 去中心化配置(任何一台服务器都可以作为入口服务器来进行读写)
4. キャッシュの侵入
キャッシュの侵入とは、存在しないデータをクエリするときに、Redis がミスするため、データがデータベース内でクエリされることを意味します。データベースでデータをクエリできない場合、データはキャッシュに書き込まれず、その結果、データが存在しなくなります。毎回データがクエリされるため、データベースにアクセスし、キャッシュの侵入が発生します。
解決:
- クエリされた null 値をキャッシュに設定します (有効期限を設定します)。
- アクセス可能なホワイトリストを設定する
- ブルームフィルター
5. キャッシュの内訳
キャッシュのブレークダウンとは、一部の人気のあるキーがアクセスされると、その時点でキーの有効期限が切れ、その結果データベースへの大量のリクエストが発生し、データベースがダウンすることを意味します。
6. キャッシュ雪崩
キャッシュなだれとは、非常に短期間に、クエリされた多数のキーの有効期限が切れ、その結果、データベースへの大量のリクエストが発生し、データベースがダウンすることを意味します。
7. 分散ロック
分散ロックにより、スナップアップやスパイクなどの同期操作が保証され、過剰販売などの問題が発生しないことが保証されます。JAVAコードは次のとおりです。
public class SellServiceImpl implements SellService {
@Autowired
private RedisTemplate redisTemplate;
/**
* 库存总量
*/
private static final Integer TOTAL = 1000;
/**
* 例如国庆大甩卖 图书大甩卖 库存 1000 件
* 存放商品信息
*/
static Map<String, Integer> products;
/**
* 抢购成功者信息
*/
static Map<String, String> orders;
static {
products = new HashMap<>();
orders = new HashMap<>();
// 设置商品名称及库存
products.put("book", TOTAL);
}
/**
* 第一种方法 synchronized 锁机制,解决高并发产生的超卖问题 但效率大大降低 不推荐使用
* 第二种方法 使用 Redis 分布式锁,解决高并发产生的超卖问题 并且效率相对高很多
*/
@Override
public String orderGoods(String productId) {
String lockValue = UUID.randomUUID().toString();
// 设置锁,并设置3s过期时间,防止手动释放锁之前程序出错,导致锁一直未释放。
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", lockValue, 3, TimeUnit.SECONDS);
// 加锁失败 说明有人正在使用
if (!lock) {
return "抢购失败,请再试试吧...";
}
//先获取商品余量
int number = products.get(productId);
if(number <= 0){
return "商品已抢购完,请您下次再来,谢谢您的理解...";
}else {
//模拟下单(不同用户拥有不同ID)
orders.put(UUID.randomUUID().toString(), productId);
//减库存
number = number - 1;
//模拟延迟
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
products.put(productId, number);
}
log.info("共抢购 {} 件,抢购详情:{}", orders.size(), orders);
// 手动解锁
String value = (String) redisTemplate.opsForValue().get("lock");
// 确保释放的锁是同一次设置的锁
if (lockValue.equals(value)){
redisTemplate.opsForValue().getOperations().delete("lock");
}
return "抢购成功!";
}
@Override
public String queryGoods(String productId) {
return "国庆图书大甩卖,库存 " +
TOTAL + " 件,现余 " +
products.get(productId) + " 件,已被抢购 " +
orders.size() + " 件";
}
}
4、Redis 統合 SpringBoot の使用
1. 依存関係を導入する
<!-- Redis依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.YMLファイルを設定する
spring:
redis:
host: 127.0.0.1
port: 6379
password: 123456
database: 0
3. RedisTemplate の構成
RedisTemplate は構成なしで使用することもできますが、キーと値はシリアル化されないため、Redis データベースに保存されるデータは 16 進エンコード形式になります。
package cn.quicklyweb.flea.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* 自定义RedisTemplate
*/
@Configuration
public class RedisTemplateConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
// 创建RedisTemplate实例
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 创建Redis的String字符串序列化对象
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
// 创建Redis的Json字符串序列化对象
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// key采用String的序列化方式
redisTemplate.setKeySerializer(stringSerializer);
// hash的key也采用String的序列化方式
redisTemplate.setHashKeySerializer(stringSerializer);
// value采用Jackson的Json序列化方式
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
// hash的value也采用Jackson的Json序列化方式
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
// 设置默认的序列化器为Jackson的Json序列化方式
redisTemplate.setDefaultSerializer(genericJackson2JsonRedisSerializer);
// 设置连接工厂
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
}
4.使用する
// 直接注入后便可操作Redis数据库了
@Autowired
private RedisTemplate redisTemplate
redisTemplate.opsForValue.set("key", "value")
redisTemplate.opsForValue.get("key")