Redis分布式缓存知识点 及 实战案例流程(附练习参考)

Redis分布式缓存知识点总结

一、Redis简介

Redis背景

我们现在的项目架构中,基本上是Web服务器(Tomcat)和数据库独立部署,独占服务器资源,随着用户数的增长,并发读写数据库,会加大数据库访问压力,导致性能的下降,严重时直接导致系统宕机。此时,我们可以在Tomcat同服务器上中增加本地缓存,并在外部增加分布式缓存,缓存热门数据。也就是通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。
Redis分布式缓存知识点 及 实战案例流程(附练习参考)_第1张图片
(图源CSDN@雨田说码)

Redis概述

Redis是一个key-value存储系统(官网:http://redis.io),是一个分布式缓存数据库。

Redis版本及参考说明

Redis的次版本号(第一个小数点后的数字)为偶数的版本是稳定版本(2.4、2.6等),奇数为非稳定版本(2.5、2.7),一般推荐在生产环境使用稳定版本。最新版本6.2.2,新增了stream的处理方式,性能更高。Redis官方是不支持windows平台的,windows版本是由微软自己建立的分支,基于官方的Redis源码上进行编译、发布、维护的,所以windows平台的Redis版本要略低于官方版本。
Redis参考网址:

Bootnb 相关:https://www.runoob.com/redis/redis-tutorial.html
Redis 官网:https://redis.io/
源码地址:https://github.com/redis/redis
Redis 在线测试:http://try.redis.io/
Redis 命令参考:http://doc.redisfans.com/

二、Redis初始操作

  1. Docker环境下启动redis服务 docker start redis666 start后面的redis666为容器名
  2. docker中查看redis服务 docker ps
  3. 查看启动的redis进程信息 ps -ef | grep redis666 如下图所示,3511/3515位进程ID
ps -ef|grep redis666
root      3511     1  0 16:29 ?   00:00:01 redis-server *:6379
root      3515     1  0 16:29 ?   00:00:01 redis-server 127.0.0.1:6380
  1. 进入redis容器 docker exec -it redis666 bash 其中redis666为容器名
  2. 登陆本地redis服务 redis-cli -p 6379 -a password
    根据需要决定是否要密码,此操作需要开启redis.conf文件中的requirepass选项
  3. 登陆远程redis服务 redis-cli -h ip -p 6379 -a password ip为远程端的ip地址
  4. 查看redis信息(需登陆以后) 127.0.0.1:6379> info #查看当前redis节点的详细配置信息
  5. 清空redis屏幕内容 127.0.0.1:6379> clear
  6. 退出redis服务 127.0.0.1:6379> exit
  7. 关闭redis服务 127.0.0.1:6379> shutdown 注意这里的shutdown会先保存更改然后退出

三、Redis数据存储操作

简易数据存取

  1. 基于查看redis中的key
127.0.0.1:6379> keys *
(empty list or set)
  1. 基于key/value形式存储数据
127.0.0.1:6379> set test1 123
OK
127.0.0.1:6379> set test2 ab
OK
127.0.0.1:6379> keys *
1) "test1"
2) "test2"
  1. 基于key获取redis中存储的数据
127.0.0.1:6379> get test1
"123"
127.0.0.1:6379> get test2
"ab"
127.0.0.1:6379> get test3
(nil)
127.0.0.1:6379>
  1. 清除redis当前数据库的数据
127.0.0.1:6379> flushdb
OK
  1. 清除redis中所有数据库的数据
127.0.0.1:6379> flushall
OK

Key的有效时间设计

实际工作中我们经常要控制redis中key的有效时长,例如秒杀操作的计时,缓存数据的有效时长等。

Expire设置生效时长(单位秒)
语法:EXPIRE key seconds

127.0.0.1:6379> set bomb tnt
OK
127.0.0.1:6379> expire bomb 10
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) 5
127.0.0.1:6379> ttl bomb
(integer) 3
127.0.0.1:6379> ttl bomb
(integer) 3
127.0.0.1:6379> ttl bomb
(integer) 2
127.0.0.1:6379> ttl bomb
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) -2
127.0.0.1:6379> ttl bomb
(integer) -2
127.0.0.1:6379>

其中,ttl查看key的剩余时间,若返回值为-2时,表示key被删除
当key不存在时,返回-2
当key存在但是没有设置剩余生存时间时,返回-1

Persist (取消时长设置)
通过persist让对特定key设置的生效时长失效。
语法:PERSIST key

127.0.0.1:6379> set bomb tnt
OK
127.0.0.1:6379> expire bomb 60
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) 49
127.0.0.1:6379> persist bomb
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) -1
127.0.0.1:6379>

其中,设置新的数据时需要重新设置该key的生存时间,重新设置值也会清除生存时间。

Pexpire (单位毫秒)
pexpire 让key的生效时长以毫秒作为计量单位,这样可以做到更精确的时间控制。例如,可应用于秒杀场景。
语法:PEXPIRE key milliseconds

127.0.0.1:6379> set bomb tnt
OK
127.0.0.1:6379> pexpire bomb 10000
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) 6
127.0.0.1:6379> ttl bomb
(integer) 3
127.0.0.1:6379> ttl bomb
(integer) -2
127.0.0.1:6379>

四、Redis常用数据类型概述

Redis作为一种key/value结构的数据存储系统,为了便于对数据进行进行管理,提供了多种数据类型。然后,基于指定类型存储我们项目中产生的数据,例如用户的登陆信息,购物车信息,商品详情信息等等。

Reids中基础数据结构包含字符串(String)、散列(Hash),列表(List),集合(Set),有序集合。工作中具体使用哪种类型要结合具体场景。

String类型操作实践

incr/incrby (increase by)

当存储的字符串是整数时,redis提供了一个实用的命令INCR,其作用是让当前键值递增,并返回递增后的值。
语法:INCR key

127.0.0.1:6379> set num 1
(integer) 1
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> keys *
1) "num"	
127.0.0.1:6379> incr num
127.0.0.1:6379>

说明,如果num不存在,则自动会创建,如果存在自动+1。

语法:INCRBY key increment
指定增长系数

127.0.0.1:6379> incrby num 2
(integer) 5
127.0.0.1:6379> incrby num 2
(integer) 7
127.0.0.1:6379> incrby num 2
(integer) 9
127.0.0.1:6379>

decr/decrby (decrease by)

减少指定的整数
DECR key 按照默认步长(默认为1)进行递减
DECRBY key decrement 按照指定步长进行递减

127.0.0.1:6379> incr num
(integer) 10
127.0.0.1:6379> decr num
(integer) 9
127.0.0.1:6379> decrby num 3

append

向尾部追加值。如果键不存在则创建该键,其值为写的value,即相当于SET key value。返回值是追加后字符串的总长度。
语法:APPEND key value

127.0.0.1:6379> keys *
1) "num"
2) "test1"
3) "test"
127.0.0.1:6379> get test
"123"
127.0.0.1:6379> append test "abc"
(integer) 6
127.0.0.1:6379> get test
"123abc"
127.0.0.1:6379>

strlen (string length)

字符串长度,返回数据的长度,如果键不存在则返回0。注意,如果键值为空串,返回也是0。
语法:STRLEN key

127.0.0.1:6379> get test
"123abc"
127.0.0.1:6379> strlen test
(integer) 6
127.0.0.1:6379> strlen tnt
(integer) 0
127.0.0.1:6379> set tnt ""
OK
127.0.0.1:6379> strlen tnt
(integer) 0
127.0.0.1:6379> exists tnt
(integer) 1
127.0.0.1:6379>

mset/mget (multiple set/get)

同时设置/获取多个键值
语法: MSET key value [key value …] MGET key [key …]

127.0.0.1:6379> flushall
OK
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> mset a 1 b 2 c 3
OK
127.0.0.1:6379> mget a b c
1) "1"
2) "2"
3) "3"
127.0.0.1:6379>

Hash类型应用实践

Redis散列类型相当于Java中的HashMap,实现原理跟HashMap一致,一般用于存储对象信息,存储了字段(field)和字段值的映射,一个散列类型可以包含最多232-1个字段。

hset/hget

语法结构

HSET key field value
HGET key field
HMSET key field value [field value…]
HMGET key field [field]
HGETALL key

HSET和HGET赋值和取值

127.0.0.1:6379> hset user username chenchen
(integer) 1
127.0.0.1:6379> hget user username
"chenchen"
127.0.0.1:6379> hset user username chen
(integer) 0
127.0.0.1:6379> keys user
1) "user"
127.0.0.1:6379> hgetall user
1) "username"
2) "chen"
127.0.0.1:6379> 
127.0.0.1:6379> hset user age 18
(integer) 1
127.0.0.1:6379> hset user address "xi'an"
(integer) 1
127.0.0.1:6379> hgetall user
1) "username"
2) "chen"
3) "age"
4) "18"
3) "address"
4) "xi'an"
127.0.0.1:6379>

HSET命令不区分插入和更新操作,当执行插入操作时HSET命令返回1,当执行更新操作时返回0。

hincrby

hash中没有decrease命令, 若要执行自减命令则hincrby xxxx -n

127.0.0.1:6379> hdecrby article total 1		   #执行会出错
127.0.0.1:6379> hincrby article total -1	   #自减命令 选择自增负数
(integer) 1
127.0.0.1:6379> hget user money             # 获取money值
"100"
127.0.0.1:6379> hincrby user money 1        # 自增1
(integer) 101
127.0.0.1:6379> hincrby user money -1       # 自减1
(integer) 100

hmset/hmget

HMSET和HMGET设置和获取对象属性

127.0.0.1:6379> hmset person username tony age 18
OK
127.0.0.1:6379> hmget person age username
1) "18"
2) "tony"
127.0.0.1:6379> hgetall person
1) "username"
2) "tony"
3) "age"
4) "18"
127.0.0.1:6379>

注意:上面HMGET字段顺序可以自行定义

hexists

属性是否存在

127.0.0.1:6379> hexists killer
(error) ERR wrong number of arguments for 'hexists' command
127.0.0.1:6379> hexists killer a
(integer) 0
127.0.0.1:6379> hexists user username
(integer) 1
127.0.0.1:6379> hexists person age
(integer) 1
127.0.0.1:6379>

hdel

删除属性

127.0.0.1:6379> hdel user age
(integer) 1
127.0.0.1:6379> hgetall user
1) "username"
2) "chen"
127.0.0.1:6379> hgetall person
1) "username"
2) "tony"
3) "age"
4) "18"
127.0.0.1:6379>

hkeys/hvals

只获取字段名HKEYS或字段值HVALS

127.0.0.1:6379> hkeys person
1) "username"
2) "age"
127.0.0.1:6379> hvals person
1) "tony"
2) "18"
2.3.8	hlen
元素个数
127.0.0.1:6379> hlen user
(integer) 1
127.0.0.1:6379> hlen person
(integer) 2
127.0.0.1:6379>

List类型应用实践

Redis的list类型相当于java中的LinkedList,其原理就就是一个双向链表。支持正向、反向查找和遍历等操作,插入删除速度比较快。经常用于实现热销榜,最新评论等的设计。

lpush

lpush(left push) 在key对应list的头部添加字符串元素

redis 127.0.0.1:6379> lpush mylist "world"
(integer) 1
redis 127.0.0.1:6379> lpush mylist "hello"
(integer) 2
redis 127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"
redis 127.0.0.1:6379>

其中,Redis Lrange 返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。 其中 0 表示列表的第一个元素, 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推

rpush

在key对应list的尾部添加字符串元素

redis 127.0.0.1:6379> rpush mylist2 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist2 "world"
(integer) 2
redis 127.0.0.1:6379> lrange mylist2 0 -1
1) "hello"
2) "world"
redis 127.0.0.1:6379>

del

删除List表

redis 127.0.0.1:6379> del mylist

linsert

在key对应list的特定位置之前或之后添加字符串元素

redis 127.0.0.1:6379> rpush mylist3 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist3 "world"
(integer) 2
redis 127.0.0.1:6379> linsert mylist3 before "world" "there"
(integer) 3
redis 127.0.0.1:6379> lrange mylist3 0 -1
1) "hello"
2) "there"
3) "world"
redis 127.0.0.1:6379>

lset

设置list中指定下标的元素值(一般用于修改操作)

redis 127.0.0.1:6379> rpush mylist4 "one"
(integer) 1
redis 127.0.0.1:6379> rpush mylist4 "two"
(integer) 2
redis 127.0.0.1:6379> rpush mylist4 "three"
(integer) 3
redis 127.0.0.1:6379> lset mylist4 0 "four"
OK
redis 127.0.0.1:6379> lset mylist4 -2 "five"
OK
redis 127.0.0.1:6379> lrange mylist4 0 -1
1) "four"
2) "five"
3) "three"
redis 127.0.0.1:6379>

lrem

从key对应list中删除count个和value相同的元素,count>0时,按从头到尾的顺序删除

redis 127.0.0.1:6379> rpush mylist5 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist5 "hello"
(integer) 2
redis 127.0.0.1:6379> rpush mylist5 "foo"
(integer) 3
redis 127.0.0.1:6379> rpush mylist5 "hello"
(integer) 4
redis 127.0.0.1:6379> lrem mylist5 2 "hello"
(integer) 2
redis 127.0.0.1:6379> lrange mylist5 0 -1
1) "foo"
2) "hello"
redis 127.0.0.1:6379>
//count<0时,按从尾到头的顺序删除
redis 127.0.0.1:6379> rpush mylist6 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist6 "hello"
(integer) 2
redis 127.0.0.1:6379> rpush mylist6 "foo"
(integer) 3
redis 127.0.0.1:6379> rpush mylist6 "hello"
(integer) 4
redis 127.0.0.1:6379> lrem mylist6 -2 "hello"
(integer) 2
redis 127.0.0.1:6379> lrange mylist6 0 -1
1) "hello"
2) "foo"
redis 127.0.0.1:6379>
//count=0时,删除全部
redis 127.0.0.1:6379> rpush mylist7 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist7 "hello"
(integer) 2
redis 127.0.0.1:6379> rpush mylist7 "foo"
(integer) 3
redis 127.0.0.1:6379> rpush mylist7 "hello"
(integer) 4
redis 127.0.0.1:6379> lrem mylist7 0 "hello"
(integer) 3
redis 127.0.0.1:6379> lrange mylist7 0 -1
1) "foo"
redis 127.0.0.1:6379>

ltrim

保留指定key 的值范围内的数据

redis 127.0.0.1:6379> rpush mylist8 "one"
(integer) 1
redis 127.0.0.1:6379> rpush mylist8 "two"
(integer) 2
redis 127.0.0.1:6379> rpush mylist8 "three"
(integer) 3
redis 127.0.0.1:6379> rpush mylist8 "four"
(integer) 4
redis 127.0.0.1:6379> ltrim mylist8 1 -1
OK
redis 127.0.0.1:6379> lrange mylist8 0 -1
1) "two"
2) "three"
3) "four"
redis 127.0.0.1:6379>

lpop

从list的头部删除元素,并返回删除元素

redis 127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"
redis 127.0.0.1:6379> lpop mylist
"hello"
redis 127.0.0.1:6379> lrange mylist 0 -1
1) "world"
redis 127.0.0.1:6379>

rpop

从list的尾部删除元素,并返回删除元素

redis 127.0.0.1:6379> lrange mylist2 0 -1
1) "hello"
2) "world"
redis 127.0.0.1:6379> rpop mylist2
"world"
redis 127.0.0.1:6379> lrange mylist2 0 -1
1) "hello"
redis 127.0.0.1:6379>

llen

返回key对应list的长度

redis 127.0.0.1:6379> llen mylist5
(integer) 2
redis 127.0.0.1:6379>

lindex

返回名称为key的list中index位置的元素

redis 127.0.0.1:6379> lrange mylist5 0 -1
1) "three"
2) "foo"
redis 127.0.0.1:6379> lindex mylist5 0
"three"
redis 127.0.0.1:6379> lindex mylist5 1
"foo"
redis 127.0.0.1:6379>

rpoplpush

从第一个list的尾部移除元素并添加到第二个list的头部,最后返回被移除的元素值。
整个操作是原子的。如果第一个list是空或者不存在返回null。

redis 127.0.0.1:6379> RPUSH mylist "hello"
(integer) 1
redis 127.0.0.1:6379> RPUSH mylist "foo"
(integer) 2
redis 127.0.0.1:6379> RPUSH mylist "bar"
(integer) 3
redis 127.0.0.1:6379> RPOPLPUSH mylist myotherlist
"bar"
redis 127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello"
2) "foo"

Set类型应用实践

Redis的Set类似Java中的HashSet,是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis中Set集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

sadd

添加元素,重复元素添加失败,返回0

127.0.0.1:6379> sadd name tony
(integer) 1
127.0.0.1:6379> sadd name hellen
(integer) 1
127.0.0.1:6379> sadd name rose
(integer) 1
127.0.0.1:6379> sadd name rose
(integer) 0

smembers

获取set内容

127.0.0.1:6379> sadd name bob jack tommy
(integer) 3
127.0.0.1:6379> smembers name
1) "bob"
2) "tommy"
3) "jack"
127.0.0.1:6379>

spop

移除并返回 集合中的一个随机元素

127.0.0.1:6379> smembers internet
1) "amoeba"
2) "redis"
3) "rabbitmq"
4) "nginx"
127.0.0.1:6379> spop internet
"rabbitmq"
127.0.0.1:6379> spop internet
"nginx"
127.0.0.1:6379> smembers internet
1) "amoeba"
2) "redis"

scard

获取成员个数

127.0.0.1:6379> scard name
(integer) 3

smove

移动一个元素到另外一个集合

127.0.0.1:6379> sadd internet amoeba nginx redis
(integer) 3
127.0.0.1:6379> sadd bigdata hadopp spark rabbitmq
(integer) 3
127.0.0.1:6379> smembers internet
1) "amoeba"
2) "redis"
3) "nginx"
127.0.0.1:6379> smembers bigdata
1) "hadopp"
2) "spark"
3) "rabbitmq"
127.0.0.1:6379> smove bigdata internet rabbitmq
(integer) 1
127.0.0.1:6379> smembers internet
1) "amoeba"
2) "redis"
3) "rabbitmq"
4) "nginx"
127.0.0.1:6379> smembers bigdata
1) "hadopp"
2) "spark"
127.0.0.1:6379>

sunion

取并集

127.0.0.1:6379> sunion internet bigdata
1) "redis"
2) "nginx"
3) "rabbitmq"
4) "amoeba"
5) "hadopp"
6) "spark"

五、Java中操作redis实践

准备工作

创建工程

创建 Maven 父工程,取名为 01-sca-redis
在此工程下面创建两个子工程,分别为 sca-jedis 和 sca-template

添加依赖

sca-jedis 项目依赖

<dependency>
    <groupId>redis.clientsgroupId>
    <artifactId>jedisartifactId>
    <version>3.5.2version>
dependency>

<dependency>
    <groupId>junitgroupId>
    <artifactId>junitartifactId>
    <version>4.12version>
    <scope>testscope>
dependency>

<dependency>
    <groupId>com.google.code.gsongroupId>
    <artifactId>gsonartifactId>
    <version>2.8.6version>
dependency>

sca-template 项目依赖

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-dependenciesartifactId>
                <version>2.3.2.RELEASEversion>
                <scope>importscope>
                <type>pomtype>
            dependency>
        dependencies>
    dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

Jedis的应用

Jedis是Java中操作redis的一个客户端,类似通过jdbc访问mysql数据库。

准备工作

第一步:从redis.io官方下载对应版本的redis.conf文件,地址如下(或者从code上去取)

https://redis.io/topics/config/

第二步:停止 redis 并删除挂载目录下 (/usr/local/docker/redis01/conf) 的 redis.conf 配置文件。
第三步:将下载的 redis.conf 文件拷贝到 redis 挂载目录 (/usr/local/docker/redis01/conf) 。
第四步:vim 打开 redis.conf 文件,注释 bind 127.0.0.1,并修改 protected-mode 的值为 no。
第五步:重启 redis 服务,并检查启动日志 (docker logs 容器id) 。

Jedis 入门实战 - 基础类型的操作

在项目的 src/test/java 目录下创建单元测试类 com.jt.JedisTests.java

测试获取 Jedis 连接

注意添加 @Test 注解时不要引错包。

package com.jt;

import org.junit.Test;
import redis.clients.jedis.Jedis;

public class JedisTests {

    @Test
    public void testGetConnection(){
        //假如不能连通,要注释掉redis.conf中 bind 127.0.0.1,
        //并将protected-mode的值修改为no,然后重启redis再试
        Jedis jedis=new Jedis("192.168.126.130",6379);
        //jedis.auth("123456");//假如在redis.conf中设置了密码
        String ping = jedis.ping();
        System.out.println(ping);
    }
}

字符串类型的操作

	/**
     * 测试jedis中对字符串类型的操作
     */
    @Test
    public void testStringOper01() throws InterruptedException {
        //1.建立连接
        Jedis jedis=new Jedis("192.168.126.130",6379);
        //2.存储数据
        jedis.set("id","10001");
        jedis.set("content","hello redis");
        jedis.set("count","10");
        //3.更新数据
        jedis.incr("count");
        jedis.expire("id",1);
        //4.删除指定数据
        jedis.del("content");
        //5.获取数据
        //TimeUnit是Java中枚举类型,SECONDS为枚举类型的实例,sleep底层会调用Thread.sleep()方法
        TimeUnit.SECONDS.sleep(1);
        String id=jedis.get("id");
        String content=jedis.get("content");
        String count=jedis.get("count");
        String result=String.format("id=%s,content=%s,count=%s",id,content,count);
        System.out.println(result);
        //6.释放资源
        jedis.close();
    }

json 数据类型的操作

	/**
     * 将一个对象(例如map)转换为json格式字符串,然后写入到redis
     */
    @Test
    public void testStringOper02(){
        //1.建立连接
        Jedis jedis=new Jedis("192.168.126.130",6379);
        //2.构建一个map对象,存储一些用户信息
        Map<String,Object> map=new HashMap<>();
        map.put("id",101);
        map.put("username","jack");
        map.put("mobile","11111111111");
        //3.将map对象内容以json字符串结构写入到redis
        Gson gson=new Gson();
        String jsonStr= gson.toJson(map);
        jedis.set("user",jsonStr);
       // jedis.expire("user",3);
        //4.将json字符串内容从redis读出并存储到map对象
        jsonStr=jedis.get("user");
        System.out.println(jsonStr);
        map=gson.fromJson(jsonStr,Map.class);
        System.out.println(map);
        //5.修改map中的数据
        map.put("mobile","2222222222");
        jsonStr= gson.toJson(map);
        jedis.set("user",jsonStr);
        //6.释放资源
        jedis.close();
    }

Hash 类型数据的操作

	/**
     * Hash类型数据的操作实践
     */
    @Test
    public void testHashOper01(){
        //1.建立连接
        Jedis jedis=new Jedis("192.168.126.130",6379);
        //2.基于hash类型存储对象信息
        jedis.hset("member","id","101");
        jedis.hset("member","username","jack");
        jedis.hset("member","mobile","3333333");
        //3.更新hash类型存储的数据
        jedis.hset("member","username","tony");
        //4.获取hash类型数据信息
        String username=jedis.hget("member","username");
        String mobile = jedis.hget("member", "mobile");
        System.out.println(username);
        System.out.println(mobile);
        //5.释放资源
        jedis.close();
    }

Hash 类型数据的操作(直接存储map对象)

	@Test
    public void testHashOper02(){
        //1.建立连接
        Jedis jedis=new Jedis("192.168.126.130",6379);
        //2.存储一篇博客信息
        Map<String,String> map=new HashMap<>();
        map.put("x","100");
        map.put("y","200");
        jedis.hset("point",map);
        //3.获取博客内容并输出
        map=jedis.hgetAll("point");
        System.out.println(map);
        //4.释放资源
        jedis.close();
    }

测试: redis 中 list 结构的应用

    /**
     * 测试:redis中list结构的应用
     * 基于FIFO(First In First Out)算法,借助redis实现一个队列
     */
    @Test
    public void testListOper01(){
        //1.建立连接
        Jedis jedis=new Jedis("192.168.126.130",6379);
        //2.存储数据
        jedis.lpush("lst1","A","B","C","C");
        //3.更新数据
        Long pos=jedis.lpos("lst1","A");//获取A元素的位置
        jedis.lset("lst1",pos,"D");//将A元素位置的内容修改为D
        //4.获取数据
        int len=jedis.llen("lst1").intValue();//获取lst1列表中元素个数
        List<String> rpop = jedis.rpop("lst1",len);//获取lst1列表中所有元素
        System.out.println(rpop);
        //5.释放资源
        jedis.close();
    }

List 类型练习(实现阻塞式队列)

    /**
     * 阻塞式队列,brpop/blpop
     */
    @Test
    public void testListOper02(){
        System.out.println(Thread.currentThread().getName());
        //1.建立连接
        Jedis jedis=new Jedis("192.168.126.130",6379);
        //2.存储数据
        jedis.lpush("lst2","A","B","C");
        //3.取数据(队列为空则阻塞)
        jedis.brpop(5,"lst2");
        jedis.brpop(5,"lst2");
        jedis.brpop(5,"lst2");
        jedis.brpop(5,"lst2");//当队列中没有内容时会阻塞
        System.out.println("waiting finish");
        //4.释放资源
        jedis.close();
    }

Set 数据类型的操作

/**
     * Set数据类型测试
     */
    @Test
    public void testSetOper01(){
        //1.建立连接
        Jedis jedis=new Jedis("192.168.126.130",6379);//TCP/IP
        //2.存储数据(不允许重复,且值无序)
        jedis.sadd("set01","A","A","B","B","C");
        //3.删除集合中元素
        jedis.srem("set01","A");
        //4.获取存储的数据
        System.out.println(jedis.scard("set01"));//集合元素的个数
        Set<String> set01 = jedis.smembers("set01");//输出集合中的成员 (点赞的成员)
        System.out.println(set01);
        //5.释放资源
        jedis.close();
    }

Jedis 入门实战 - 连接池 JedisPool 应用

我们直接基于Jedis访问redis时,每次获取连接,释放连接会带来很大的性能开销,可以借助Jedis连接池,重用创建好的连接,来提高其性能,简易应用方式如下:

创建单元测试类 com.jt.JedisPoolTests 类

package com.jt;

import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;


public class JedisPoolTests {
    @Test
   public void testJedisPool(){
        //定义连接池的配置
        JedisPoolConfig config=new JedisPoolConfig();
        config.setMaxTotal(1000);//最大连接数
        config.setMaxIdle(60);//最大空闲时间(连接不用了要释放)
        //创建连接池
        JedisPool jedisPool=
        new JedisPool(config,"192.168.126.130",6379);
        //从池中获取一个连接
        Jedis resource = jedisPool.getResource();
        resource.auth("123456");
        //通过jedis连接存取数据
        resource.set("class","cgb2004");
        String clazz=resource.get("class");
        System.out.println(clazz);
        //将链接返回池中
        resource.close();
        //关闭连接池
        jedisPool.close();
    }
}

RedisTemplate应用

RedisTemplate 为 SpringBoot 工程中操作 redis 数据库的一个 Java 对象
此对象封装了对 redis 的一些基本操作。

准备工作

创建工程配置文件application.yml,其内容如下:

spring:
  redis:
    host: 192.168.128.129  # 写自己虚拟机的 ip 地址
    port: 6379

创建工程启动类 RedisApplication

package com.jt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

StringRedisTemplate 应用实战

StringRedisTemplate 是一个专门用于操作 redis 字符串类型数据的一个对象,其应用方式如下

package com.jt;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@SpringBootTest
public class StringRedisTemplateTests {

    /**StringRedisTemplate 对象是spring data模块推出的一个用于
     * 操作redis字符串类型数据的一个API对象,底层序列方式使用的是
     * StringRedisSerializer对象
     * */
    @Autowired
    private StringRedisTemplate redisTemplate;
    /**
     * 基于StringRedisTemplate对象操作hash数据类型
     */
    @Test
    void testHashOper01(){
        //1.获取操作hash类型数据的对象
        HashOperations<String, Object, Object> ho
                = redisTemplate.opsForHash();
        //2.操作hash类型数据
        ho.put("user","id","100");
        ho.put("user","username","jack");
        ho.put("user","state","true");
        List<Object> user = ho.values("user");
        System.out.println(user);
    }

    /**以json格式存储一个对象到redis数据库
     * 提示:不能使用Gson,可以使用jackson*/
    @Test
    void testStringOper02() throws JsonProcessingException {
        //1.创建一个map,用于存储对象数据
        Map<String,String> map=new HashMap<>();
        map.put("id","101");
        map.put("username","jack");
        //2.将map转换为json格式字符串
        String jsonStr =
        new ObjectMapper().writeValueAsString(map);
        //3.将json字符串借助StringRedisTemplate存储到redis,并读取
        ValueOperations<String, String> vo =
                redisTemplate.opsForValue();
        vo.set("userJsonStr",jsonStr);
        jsonStr=vo.get("userJsonStr");
        map= new ObjectMapper().readValue(jsonStr,Map.class);
        System.out.println(map);
    }
    /**
     * 字符串数据基本操作实践
     * @throws InterruptedException
     */
    @Test
    void testStringOper01() throws InterruptedException {
       //1.获取一个操作redis字符串的值对象
        ValueOperations<String, String> vo =
                redisTemplate.opsForValue();
        //2.基于这个值对象操作redis中的字符串数据
        vo.set("x","100");
        vo.increment("x");
        vo.set("y","200",1, TimeUnit.SECONDS);//设置key的失效时间
        TimeUnit.SECONDS.sleep(1);//阻塞1秒
        String x = vo.get("x");
        System.out.println(x);
        String y=vo.get("y");
        System.out.println(y);
    }

    @Test
    void testConnection(){
        RedisConnection connection =
                redisTemplate.getConnectionFactory()
                        .getConnection();
        String ping = connection.ping();
        System.out.println(ping);
    }
}

RedisTemplate 应用实战

RedisTemplate是一个专门用于实现对远端redis中复杂数据的操作的对应,应用案例如下

package com.jt;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@SpringBootTest
public class RedisTemplateTests {
    /**RedisTemplate对象也Spring data模块提供的一个操作redis数据库数据的API对象,
     * 此对象主要用于操作redis数据库中的复杂数据类型数据,默认序列化方式为
     * JdkSerializationRedisSerializer*/
    @Autowired
    private RedisTemplate redisTemplate;//将来可以按照自己的业务需求定制RedisTempalte

    @Test
    void testSetData(){
        SetOperations setOperations=redisTemplate.opsForSet();
        setOperations.add("setKey1", "A","B","C","C");
        Object members=setOperations.members("setKey1");
        System.out.println("setKeys="+members);
        //........
    }
    @Test
    void testListData(){
        //向list集合放数据
        ListOperations listOperations = redisTemplate.opsForList();
        listOperations.leftPush("lstKey1", "100"); //lpush
        listOperations.leftPushAll("lstKey1", "200","300");
        listOperations.leftPush("lstKey1", "100", "105");
        listOperations.rightPush("lstKey1", "700");
        Object value= listOperations.range("lstKey1", 0, -1);
        System.out.println(value);
        //从list集合取数据
        Object v1=listOperations.leftPop("lstKey1");//lpop
        System.out.println("left.pop.0="+v1);
        value= listOperations.range("lstKey1", 0, -1);
        System.out.println(value);
    }

    @Test
    void testHashOper01(){
        HashOperations ho = redisTemplate.opsForHash();
        ho.put("blog", "id", 101);
        ho.put("blog", "title", "redis template");
        List blog = ho.values("blog");

        System.out.println(blog);
        Map<String,Object> map=new HashMap<String,Object>();
        map.put("id", 201);
        map.put("name", "redis");
        ho.putAll("tag",map);
        Object name= ho.get("tag", "name");
        System.out.println(name);
    }

    @Test
    void testStringOper01(){
        //默认采用了JDK的序列化方式,存储key/value默认会将key/value都转换为字节再进行存储
        //redisTemplate.setKeySerializer(new JdkSerializationRedisSerializer());
        //自己设置序列化方式
        //redisTemplate.setKeySerializer(new StringRedisSerializer());
        //redisTemplate.setValueSerializer(new StringRedisSerializer());
        ValueOperations vo =
                redisTemplate.opsForValue();
        vo.set("token", UUID.randomUUID().toString());
        Object token = vo.get("token");
        System.out.println(token);
    }
	@Test
    void testFlushdb(){
        redisTemplate.execute(new RedisCallback() {
            @Override
            public Object doInRedis(RedisConnection redisConnection) 
            								throws DataAccessException {
               //redisConnection.flushDb();
                redisConnection.flushAll();
                return "flush ok";
            }
        });
    }
}

定制RedisTemplate对象(拓展)

对于系统默认的RedisTemplate默认采用的是JDK的序列化机制,假如我们不希望实用JDK的序列化,可以采用的定制RedisTemplate,并采用自己指定的的序列化方式。

在工程中创建 com.jt.demos.RedisConfig 文件。

package com.jt.redis.demos;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        //1.构建RedisTemplate对象
        RedisTemplate<Object,Object> redisTemplate=new RedisTemplate<>();
        //2.设置连接工厂
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //3.定义序列化方式(在这里选择jackson)
        Jackson2JsonRedisSerializer redisSerializer= 
        				new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper=new ObjectMapper();
        //设置要序列化的域(属性)
        //any表示任意级别访问修饰符修饰的属性 private,public,protected
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //启动输入域检查(类不能是final修饰的)
        objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(),
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY);
        redisSerializer.setObjectMapper(objectMapper);
        //4.设置RedisTemplate的序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(redisSerializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(redisSerializer);
        //spring规范中假如修改bean对象的默认特性,建议调用一下afterPropertiesSet()
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

创建Blog对象,然后基于RedisTemplate进行序列化实践,Blog代码如下

package com.jt.redis.pojo;

import java.io.Serializable;

public class Blog implements Serializable {//{"id":10,"title":"redis"}
    private static final long serialVersionUID = -6721670401642138021L;
    private Integer id;
    private String title;
    public Blog(){
        System.out.println("Blog()");
    }
    public Blog(Integer id,String title){
        this.id=id;
        this.title=title;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
    @Override
    public String toString() {
        return "Blog{" +
                "id=" + id +
                ", title='" + title + '\'' +
                '}';
    }
}

在RedisTemplateTests类中添加如下单元测试方法,进行测试

@Test
void testJsonOper() throws JsonProcessingException {
    ValueOperations valueOperations = redisTemplate.opsForValue();
    Blog blog=new Blog(10,"study redis");
    valueOperations.set("blog",blog);//序列化
    blog=(Blog)valueOperations.get("blog");//反序列化
    System.out.println("blog="+blog);
}

六、Java 中 Redis 项目工程实践

七、Redis 数据持久化实践

八、Redis 事务处理实践

九、Redis高级特性应用实践

十、Java中本地缓存实现(拓展)

你可能感兴趣的:(CGB课程学习总结练习,redis,缓存,数据库)