Dark Horse プログラマー Redis スタディ ノート -- Redis の基本

最初に書く

  • 最高の読書体験のために、私の個人ブログにアクセスしてこの記事をご覧ください: https://cyborg2077.github.io/2022/10/21/RedisBasic/

Redisを理解する

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

  • キー値
  • NoSQL

その中で键值型、Redis に格納されるデータは Key-Value キーと値のペアの形式で格納され、Value は文字列、値、さらには Json など、さまざまな形式にすることができます

NoSQL は、従来のリレーショナル データベースとは大きく異なるデータベースです。

NoSQLを知る

NoSqlNot Only Sql (SQL だけではない)、または No Sql (非 SQL) データベースとして変換できます。従来のリレーショナル データベースとは大きく異なる特殊なデータベースであるため、 とも呼ばれます非关系型数据库

構造化と非構造化

従来のリレーショナル データベースは構造化されたデータです. 各テーブルには、作成時にフィールド名、フィールドのデータ型、フィールドの制約などの厳密な制約情報があります. 挿入されたデータは、これらの制約に従う必要があります

一方、NoSQL では、キー値型、ドキュメント型、さらにはグラフ形式など、データベース形式に制限がありません。

関連付けと非関連付け

従来のデータベースではテーブル間の関連付けがよくあります. たとえば、外部キー制約
と非リレーショナル データベースには関連付け関係がありません. 関係を維持するには、コード内のビジネス ロジックに依存するか、データ間の結合を使用します.

{
  id: 1,
  name: "张三",
  orders: [
    {
       id: 1,
       item: {
	 id: 10, title: "荣耀6", price: 4999
       }
    },
    {
       id: 2,
       item: {
	 id: 20, title: "小米11", price: 3999
       }
    }
  ]
}

たとえば、ここで Zhang San と 2 つの携帯電話注文の関係を維持するには、これら 2 つの製品を Zhang San の注文ドキュメントに重複して保存する必要がありますが、これは十分に洗練されていないため、ビジネス ロジックを使用して関係を維持することをお勧めします。

照会モード

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

SELECT id, age FROM tb_user WHERE id = 1

異なる非リレーショナル データベースのクエリ構文は大きく異なります。

Redis:  get user:1
MongoDB: db.user.find({_id: 1})
elasticsearch:  GET http://localhost:9200/users/1

事務

従来のリレーショナル データベースはトランザクションの ACID 原則 (原子性、一貫性、独立性、耐久性) を満たすことができ、
非リレーショナル データベースはトランザクションをサポートしないか、ACID の特性を保証できず、コストの一貫性しか達成できません。

要約する

SQL NoSQL
データ構造 構造化された 構造化されていない
データの関連付け 関連した 無関係
照会モード SQL クエリ 非 SQL
取引の特徴 ベース
保管方法 ディスク メモリー
スケーラビリティ 垂直 レベル
使用シーン 1) データ構造が固定されている
2) 一貫性とセキュリティの要件が高くない
1) データ構造が固定されていない
2) 関連するビジネスには、データのセキュリティと一貫性に対する高い要件がある
3) パフォーマンス要件
  • 保管方法
    • リレーショナル データベースはディスク ストレージに基づいており、大量のディスク IO が発生し、パフォーマンスに一定の影響を与えます。
    • 非リレーショナル データベースの場合、それらの操作はメモリに依存して動作し、メモリの読み取りおよび書き込み速度は非常に高速になり、パフォーマンスは当然向上します。
  • スケーラビリティ
    • リレーショナル データベース クラスタ モードは一般にマスター スレーブであり、マスター スレーブ データは一貫性があり、垂直拡張と呼ばれるデータ バックアップの役割を果たします。
    • 非リレーショナル データベースは、データを分割して別のマシンに保存できるため、大量のデータを保存し、限られたメモリ サイズの問題を解決できます。これを水平スケーリングと呼びます。
    • リレーショナル データベースは、テーブル間の関係のために、データ クエリに多くの問題を引き起こします。

Redis に会う

Redis は 2009 年に誕生しました. 完全な名前は Remote Dictionary Server です. メモリベースのキー値 NoSQL データベースです.

特徴:

  • Key-Value (Key-Value) 型、Value はさまざまなデータ構造をサポートし、機能が豊富
  • シングルスレッド、各コマンドはアトミック
  • 低レイテンシ、高速 (メモリベース、IO 多重化、優れたエンコーディング)
  • データの永続性をサポート
  • マスタースレーブクラスターとシャードクラスターをサポート
  • 多言語クライアントをサポート

作者: アンティレス

Redis 公式サイト: https://redis.io/

Redis をインストールする

Redisのインストールについては、前回の記事で詳しく説明したのでここでは割愛します

{% リンク Redis の使用開始、https://cyborg2077.github.io/2022/10/17/ReggieRedis/、https://pic1.imgdb.cn/item/6335135c16f2c2beb100182d.jpg %}

Redis デスクトップ クライアント

Redisをインストールすると、Redisを操作してデータのCRUDを実現できます。これには、以下を含む Redis クライアントを使用する必要があります。

  • コマンド ライン クライアント
  • グラフィカル デスクトップ クライアント
  • プログラミング クライアント

Redis コマンド ライン クライアント

Redis のインストールが完了すると、次のように使用できるコマンド ライン クライアント redis-cli が付属しています。

redis-cli [options] [commonds]

一般的なオプションは次のとおりです。

  • -h 127.0.0.1: 接続する redis ノードの IP アドレスを指定します。デフォルトは 127.0.0.1 です。
  • -p 6379: 接続する redis ノードのポートを指定します。デフォルトは 6379 です。
  • -a 123321:redisのアクセスパスワードを指定

コモンズは Redis 操作コマンドです。たとえば、次のとおりです。

  • ping: redis サーバーでハートビート テストを実行すると、サーバーは正常に `pong を返します

グラフィカル デスクトップ クライアント

インストール パッケージ: https://github.com/lework/RedisDesktopManager-Windows/releases

Redis にはデフォルトで 16 のウェアハウスがあり、0 から 15 までの番号が付けられています。ウェアハウスの数は構成ファイルで設定できますが、16 を超えることはなく、ウェアハウス名をカスタマイズすることはできません。

redis-cli に基づいて Redis サービスに接続している場合は、select コマンドを使用してデータベースを選択できます。

# 选择0号数据库
select 0

Redis 共通コマンド

Redis は典型的なキーと値のデータベースであり、キーは一般に文字列であり、値にはさまざまなデータ型が含まれています

Redis 共通コマンド

一般的に使用される一般的なコマンドは次のとおりです。

注文 説明
キーパターン 指定されたパターンに一致するすべてのキーを見つける
EXIST キー 指定されたキーが存在するかどうかを確認します
タイプキー キーによって格納された値の型を返します
TTL キー 指定されたキーの残りの存続時間 (TTL、存続時間) を秒単位で返します。
DELキー このコマンドは、キーが存在する場合にキーを削除するために使用されます
  • KEYS: テンプレートに一致するすべてのキーを表示します
    • It is not recommended to use it on production environment devices, because Redis is single-threaded, and it will block other commands when running queries. データ量が多い場合、ファジー クエリに KEYS を使用するのは非常に非効率的です。
  • DEL: 指定したキーを削除します
    • 複数のキーを削除することもできます。DEL name age名前と年齢の両方が削除されます
  • EXISTS: キーが存在するかどうかを判断します
    • EXISTS name、存在する場合は 1 を返し、存在しない場合は 0 を返します
  • EXPIRE: キーの有効期限を設定すると、有効期限が切れたキーは自動的に削除されます
    • EXPIRE name 20、名前の有効期間を20秒に設定し、有効期限が切れると自動的に削除されます
  • TTL: キーの残りの有効期間を表示します (Time-To-Live)
    • TTL name、名前の残りの有効期間を確認します。有効期間が設定されていない場合は、-1 を返します

文字列型

String 型、つまり文字列型は、Redis で最も単純な格納型で、
その値は文字列ですが、文字列の形式によって 3 つの型に分けることができます。

  • string: 通常の文字列
  • int: 整数型、自己インクリメントおよび自己デクリメント操作を行うことができます
  • float: 自己インクリメントと自己デクリメントの演算が可能な浮動小数点型
    どのような形式でも、最下層はバイト配列の形式で格納されますが、エンコード方法が異なり、文字列の最大スペースtype は 512M を超えることはできません

文字列共通コマンド

String の一般的なコマンドは次のとおりです。

注文 説明
設定 String 型の既存のキーと値のペアを追加または変更します
得る キーに従って文字列型の値を取得する
多くの 複数の文字列型のキーと値のペアをバッチで追加する
MGET 複数のキーに応じて文字列型の値を複数取得する
増分 整数キーを 1 インクリメントする
インクビー 整数キーを自動インクリメントさせ、ステップ値を指定します。例: incrby num 2、num 値を 2 ずつ自動インクリメントさせます。
増分フロート 指定されたステップ値で浮動小数点数をインクリメントします
SETNX キーが存在しない場合は、文字列型のキーと値のペアを追加します。それ以外の場合は実行されません。これは、実際の増加として理解できます
セブン 文字列型のキーと値のペアを追加し、有効期間を指定します

キー構造

  • Redis には MySQL のテーブルに似た概念がないため、異なるタイプのキーをどのように区別するのでしょうか?
  • 例: ユーザーと製品の情報を Redis に保存する必要がある. ID が 1 のユーザーと、たまたま ID が 1 の製品があり、このときに ID をキーとして使用すると、競合が発生します。 。 私は何をすべきか?
  • キーに接頭辞を付けることで区別できますが、この接頭辞は無造作に付けられるものではなく、一定の仕様があります
    • Redis のキーは、:複数の単語で区切られた階層構造を形成する複数の単語を許可します。形式は次のとおりです。
    项目名:业务名:类型:id
    
    • この形式は固定されていません。必要に応じてエントリを削除/追加できます。これにより、さまざまなデータ型のデータを区別できるため、キーの競合を回避できます。
    • たとえば、私たちのプロジェクトは reggie と呼ばれ、ユーザーと料理の 2 つの異なるタイプのデータがあり、次のようにキーを定義できます。
      • ユーザー関連のキー:reggie:user:1
      • 料理関連キー:reggie:dish:1
  • 値が User オブジェクトなどの Java オブジェクトの場合、オブジェクトを JSON 文字列にシリアライズして格納できます。
価値
レジー:ユーザー:1 {“ID”:1, “名前”: “ジャック”, “年齢”: 21}
レジー:ディッシュ:1 {"id": 1, "name": "チョウザメ鍋", "price": 4999}
  • また、Redis のデスクトップ クライアントでは、同じプレフィックスが階層構造としても使用されるため、データが階層的に見え、関係が明確になります。

ハッシュタイプ

  • Java の HashMap 構造に似た、値が順序付けられていないディクショナリである、ハッシュとも呼ばれるハッシュ型
  • String 構造は、オブジェクトを JSON 文字列にシリアル化した後に格納されます。これは、オブジェクトの属性の値を変更する場合に非常に不便です。
価値
レジー:ユーザー:1 {“ID”:1, “名前”: “ジャック”, “年齢”: 21}
レジー:ディッシュ:1 {"id": 1, "name": "チョウザメ鍋", "price": 4999}
  • ハッシュ構造は、各フィールドをオブジェクトに個別に格納でき、単一のフィールドに対して CRUD を実行できます
価値
分野 価値
レジー:ユーザー:1 名前 ジャック
年  21

レジー:ユーザー:2
名前  薔薇
年  18
  • ハッシュの一般的なコマンドは次のとおりです。
注文 説明
HSET キー フィールドの値 ハッシュ型キーのフィールドの値を追加または変更します
HGET キー フィールド ハッシュ型キーのフィールド値を取得する
HMSET 複数のハッシュ型キーのフィールド値を一括追加
HMGET 複数のハッシュ型キーのフィールド値をまとめて取得する
HGETALL ハッシュ型キーのすべてのフィールドと値を取得する
HKEYS ハッシュ タイプ キーのすべてのフィールドを取得する
ヒンクリビー ハッシュ型キーのフィールド値を自動インクリメントさせ、ステップ サイズを指定する
HSETNX フィールドが存在しない場合は、ハッシュ型キーのフィールド値を追加します。存在しない場合は実行されません

リスト型

  • Redis中的List类型与Java中的LinkedList类似,可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。

  • 特征也与LinkedList类似:

    • 有序
    • 元素可以重复
    • 插入和删除快
    • 查询速度一般
  • 常用来存储一个有序数据,例如:朋友圈点赞列表,评论列表等。

  • List的常见命令有:

命令 描述
LPUSH key element … 向列表左侧插入一个或多个元素
LPOP key 移除并返回列表左侧的第一个元素,没有则返回nil
RPUSH key element … 向列表右侧插入一个或多个元素
RPOP key 移除并返回列表右侧的第一个元素
LRANGE key star end 返回一段角标范围内的所有元素
BLPOP和BRPOP 与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil

Set类型

  • Redis的Set结构与Java中的HashSet类似,可以看做是一个value为null的HashMap。因为也是一个hash表,因此具备与HashSet类似的特征:
    • 无序
    • 元素不可重复
    • 查找快
    • 支持交集、并集、差集等功能
  • Set的常见命令有:
命令 描述
SADD key member … 向set中添加一个或多个元素
SREM key member … 移除set中的指定元素
SCARD key 返回set中元素的个数
SISMEMBER key member 判断一个元素是否存在于set中
SMEMBERS 获取set中的所有元素
SINTER key1 key2 … 求key1与key2的交集
SUNION key1 key2 … 求key1与key2的并集
SDIFF key1 key2 … 求key1与key2的差集

{% note info no-icon %}
练习题:

  1. 将下列数据用Redis的Set集合来存储:
  • 张三的好友有:李四、王五、赵六
127.0.0.1:6379> sadd zhangsan lisi wangwu zhaoliu
(integer) 3
  • 李四的好友有:王五、麻子、二狗
127.0.0.1:6379> sadd lisi wangwu mazi ergou
(integer) 3
  1. 利用Set的命令实现下列功能:
  • 计算张三的好友有几人
127.0.0.1:6379> scard zhangsan
(integer) 3
  • 计算张三和李四有哪些共同好友
127.0.0.1:6379> sinter zhangsan lisi
1) "wangwu"
  • 查询哪些人是张三的好友却不是李四的好友
127.0.0.1:6379> sdiff zhangsan lisi
1) "zhaoliu"
2) "lisi"
  • 查询张三和李四的好友总共有哪些人
127.0.0.1:6379> sunion zhangsan lisi
1) "wangwu"
2) "zhaoliu"
3) "ergou"
4) "lisi"
5) "mazi"
  • 判断李四是否是张三的好友
127.0.0.1:6379> sismember zhangsan lisi
(integer) 1
  • 判断张三是否是李四的好友
127.0.0.1:6379> sismember lisi zhangsan
(integer) 0
  • 将李四从张三的好友列表中移除
127.0.0.1:6379> srem zhangsan lisi
(integer) 1

{% endnote %}

SortedSet类型

  • Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加 hash表。
  • SortedSet具备下列特性:
    • 可排序
    • 元素不重复
    • 查询速度快
  • 因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。
  • SortedSet的常见命令有:
命令 描述
ZADD key score member 添加一个或多个元素到sorted set ,如果已经存在则更新其score值
ZREM key member 删除sorted set中的一个指定元素
ZSCORE key member 获取sorted set中的指定元素的score值
ZRANK key member 获取sorted set 中的指定元素的排名
ZCARD key 获取sorted set中的元素个数
ZCOUNT key min max 统计score值在给定范围内的所有元素的个数
ZINCRBY key increment member 让sorted set中的指定元素自增,步长为指定的increment值
ZRANGE key min max 按照score排序后,获取指定排名范围内的元素
ZRANGEBYSCORE key min max 按照score排序后,获取指定score范围内的元素
ZDIFF、ZINTER、ZUNION 求差集、交集、并集

{% note warning no-icon %}
注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可,例如:

  • 升序获取sorted set 中的指定元素的排名:ZRANK key member
  • 降序获取sorted set 中的指定元素的排名:ZREVRANK key memeber
    {% endnote %}

{% note info no-icon %}

  • 练习题:
    • 将班级的下列学生得分存入Redis的SortedSet中:
    • Jack 85, Lucy 89, Rose 82, Tom 95, Jerry 78, Amy 92, Miles 76
    127.0.0.1:6379> zadd stu 85 Jack 89 Lucy 82 Rose 95 Tom 78 Jerry 92 Amy 76 Miles
    (integer) 7
    
    • 并实现下列功能:
      • 删除Tom同学
      127.0.0.1:6379> zrem stu Tom
      (integer) 1
      
      • 获取Amy同学的分数
      127.0.0.1:6379> zscore stu Amy
      "92"
      
      • 获取Rose同学的排名
      127.0.0.1:6379> zrank stu Rose
      (integer) 2
      
      • 查询80分以下有几个学生
      127.0.0.1:6379> zcount stu 0 80
      (integer) 2
      
      • 给Amy同学加2分
      127.0.0.1:6379> zincrby stu 2 Amy
      "94"
      
      • 查出成绩前3名的同学
      127.0.0.1:6379> zrange stu 0 2
      1) "Miles"
      2) "Jerry"
      3) "Rose"
      
      • 查出成绩80分以下的所有同学
      127.0.0.1:6379> zrangebyscore stu 0 80
      1) "Miles"
      2) "Jerry"
      

{% endnote %}

Redis的Java客户端

  • 目前主流的Redis的Java客户端有三种
    • Jedis和Lettuce:这两个主要是提供了Redis命令对应的API,方便我们操作Redis,而SpringDataRedis又对这两种做了抽象和封装,因此我们后期会直接以SpringDataRedis来学习。
    • Redisson:是在Redis基础上实现了分布式的可伸缩的java数据结构,例如Map、Queue等,而且支持跨进程的同步机制:Lock、Semaphore等待,比较适合用来实现特殊的功能需求。

Jedis客户端

快速入门

  • 使用Jedis的步骤
    1. 导入Jedis的maven坐标
    <!--jedis-->
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.7.0</version>
    </dependency>
    <!--单元测试-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.7.0</version>
        <scope>test</scope>
    </dependency>
    
    1. 建立连接
      新建一个单元测试类
    private Jedis jedis;
    
    @BeforeEach
    void setUp() {
        //1. 建立连接
        jedis = new Jedis("101.42.225.160", 6379);
        //2. 设置密码
        jedis.auth("root");
        //3. 选择库
        jedis.select(0);
    }
    
    1. 测试
    @Test
    void testString(){
        jedis.set("name","Kyle");
        String name = jedis.get("name");
        System.out.println("name = " + name);
    }
    
    @Test
    void testHash(){
        jedis.hset("reggie:user:1","name","Jack");
        jedis.hset("reggie:user:2","name","Rose");
        jedis.hset("reggie:user:1","age","21");
        jedis.hset("reggie:user:2","age","18");
        Map<String, String> map = jedis.hgetAll("reggie:user:1");
        System.out.println(map);
    }
    
    1. 释放资源
    @AfterEach
    void tearDown(){
        if (jedis != null){
            jedis.close();
        }
    }
    

连接池

  • Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedis的直连方式。
  • 新建一个com.blog.util,用于存放我们编写的工具类
  • 但后面我们使用SpringDataRedis的时候,可以直接在yml配置文件里配置这些内容
public class JedisConnectionFactory {

    private static JedisPool jedisPool;

    static {
        // 配置连接池
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(8);
        poolConfig.setMaxIdle(8);
        poolConfig.setMinIdle(0);
        poolConfig.setMaxWaitMillis(1000);
        // 创建连接池对象,参数:连接池配置、服务端ip、服务端端口、超时时间、密码
        jedisPool = new JedisPool(poolConfig, "101.42.225.160", 6379, 1000, "root");
    }

    public static Jedis getJedis(){
        return jedisPool.getResource();
    }
}
  • 之后我们的测试类就可以修改为如下
@SpringBootTest
class RedisTestApplicationTests {

    private Jedis jedis = JedisConnectionFactory.getJedis();

    @Test
    void testString(){
        jedis.set("name","Kyle");
        String name = jedis.get("name");
        System.out.println("name = " + name);
    }

    @Test
    void testHash(){
        jedis.hset("reggie:user:1","name","Jack");
        jedis.hset("reggie:user:2","name","Rose");
        jedis.hset("reggie:user:3","name","Kyle");
        jedis.hset("reggie:user:1","age","21");
        jedis.hset("reggie:user:2","age","18");
        jedis.hset("reggie:user:3","age","18");
        Map<String, String> map = jedis.hgetAll("reggie:user:1");
        System.out.println(map);
    }

    @AfterEach
    void tearDown(){
        if (jedis != null){
            jedis.close();
        }
    }
}

SpringDataRedis客户端

  • SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis

  • 官网地址:https://spring.io/projects/spring-data-redis

    • 提供了对不同Redis客户端的整合(Lettuce和Jedis)
    • 提供了RedisTemplate统一API来操作Redis
    • 支持Redis的发布订阅模型
    • 支持Redis哨兵和Redis集群
    • 支持基于Lettuce的响应式编程
    • 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
    • 支持基于Redis的JDKCollection实现
  • SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:

API 返回值类型 说明
redisTemplate.opsForValue() ValueOperations 操作String类型数据
redisTemplate.opsForHash() HashOperations 操作Hash类型数据
redisTemplate.opsForList() ListOperations 操作List类型数据
redisTemplate.opsForSet() SetOperations 操作Set类型数据
redisTemplate.opsForzSet() ZSetOperations 操作SortedSet类型数据
redisTemplate 通用的命令

快速入门

SpringBoot已经提供了对SpringDataRedis的支持,使用起来非常简单

  1. 导入依赖,
<!--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>
<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
  1. 配置Redis
spring:
  redis:
    host: 101.42.225.160
    port: 6379
    password: root
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: 100ms
  1. 注入RedisTemplate
    因为有了SpringBoot的自动装配,我们可以拿来就用
@Autowired
private RedisTemplate redisTemplate;
  1. 编写测试方法
@Test
void stringTest(){
    redisTemplate.opsForValue().set("username","David");
    String username = (String) redisTemplate.opsForValue().get("username");
    System.out.println(username);
}

自定义序列化

  • RedisTemplate可以接收任意Object作为值写入Redis
  • 只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的

\xAC\xED\x00\x05t\x00\x06\xE5\xBC\xA0\xE4\xB8\x89

  • 缺点:

    • 可读性差
    • 内存占用较大
  • 我们可以自定义RedisTemplate的序列化方式,代码如下
    com.blog.config包下编写对应的配置类

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        // 创建RedisTemplate对象
        RedisTemplate<String, Object> 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;
    }
}
  • 我们编写一个User类,并尝试将其创建的对象存入Redis,看看是什么效果
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private Integer age;
}
  • 测试方法
@Test
void stringTest(){
    redisTemplate.opsForValue().set("userdata",new User("张三",18));
}
  • 这里采用了JSON序列化来代替默认的JDK序列化方式。最终结果如下:
{
  "@class": "com.blog.entity.User",
  "name": "张三",
  "age": 18
}
  • 整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。
  • 所以肯定会有更好的方法

StringRedisTemplate

  • 为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。
  • 因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis就不会将class信息写入Redis了
  • 这种用法比较普遍,因此SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。源码如下
public class StringRedisTemplate extends RedisTemplate<String, String> {
    public StringRedisTemplate() {
        this.setKeySerializer(RedisSerializer.string());
        this.setValueSerializer(RedisSerializer.string());
        this.setHashKeySerializer(RedisSerializer.string());
        this.setHashValueSerializer(RedisSerializer.string());
    }
  • 省去了我们自定义RedisTemplate的序列化方式的步骤(可以将之前配置的RedisConfig删除掉),而是直接使用:
@Test
void stringTest() throws JsonProcessingException {
    //创建对象
    User user = new User("张三", 18);
    //手动序列化
    String json = mapper.writeValueAsString(user);
    //写入数据
    stringRedisTemplate.opsForValue().set("userdata", json);
    //获取数据
    String userdata = stringRedisTemplate.opsForValue().get("userdata");
    //手动反序列化
    User readValue = mapper.readValue(userdata, User.class);
    System.out.println(readValue);
}
  • 存入Redis中是这样的
{
  "name": "张三",
  "age": 18
}

おすすめ

転載: blog.csdn.net/qq_33888850/article/details/129770025