大型项目技术栈第八讲 Redis

时间:2022-07-25
本文章向大家介绍大型项目技术栈第八讲 Redis,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Redis


  • Redis简介
  • Redis安装
  • redis常用配置说明
  • Redis的键key
  • Redis的值value(数据结构类型)
  • Jedis连接redis服务器
  • spring整合redis

一、Redis简介

1.概念

Redis是一个开源的,使用ANSI C 编写,基于内存的且支持持久化,高性能的Key-Value的NoSQL数据库。

我们已经学过mysql等数据库,为什么学习redis?

redis将数据写入内存,不需要通过I/O流读取数据,效率相对较高。

在项目中使用redis,主要是从两个角度去考虑:性能和并发,这是当前互联网发展过程中首要考虑的两个重难题。当然除去这两个要点之外,redis还具备可以做分布式锁等其他功能,但是如果只是为了分布式锁这些其他功能,完全还有其他中间件(如zookpeer等)代替,并不是非要使用redis。Redis还易于扩展、支持丰富的数据类型存储。

(一)性能

如下图所示,我们在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,因为Redis是基于内存级别的缓存,这样使得请求能够迅速从缓存中得到响应。相比传统请求数据库,响应速度大幅度提高。

(二)并发

如下图所示,在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用redis做一个缓冲操作,并且Redis支持高性能的主从复制的集群策略,这样大大提高满足高并发访问及快速响应。请求优先访问到redis请求数据,从而避免高并发情况下直接访问数据库。

(三)redis支持存储数据类型丰富,以及每种数据类型的使用场景

我们一般用redis做什么?

(1)缓存数据

(2)做数据库使用

我们要学什么东西?

(1)redis的数据结构类型

(2)数据是如何持久化

2、特点

​ 支持数据结构类型丰富,有如字符串(strings),散列(hashes),列表(lists),集合(sets),有序集合(sorted sets)与范围查询,bitmaps,hyperloglogs和地理空间(geospatial)索引半径查询。

丰富的支持主流语言的客户端,C、C++、Python、Erlang、R、C#、Java、PHP、Objective-C、Perl、Ruby、Scala、Go、JavaScript

3、数据模型(重点)

Redis的数据结构类型,指的是redis值的结构类型。

二、Redis安装

1、redis安装

Windows的Redis安装包需要到以下GitHub链接找到。

链接

打开网站后,找到Release,点击前往下载页面。

2.下载

在下载网页中,找到最后发行的版本(此处是3.2.100)。

找到Redis-x64-3.2.100.msi和Redis-x64-3.2.100.zip,点击下载。

这里说明一下,第一个是msi微软格式的安装包,第二个是压缩包。

3.运行安装包

双击刚下载好的msi格式的安装包(Redis-x64-3.2.100.msi)开始安装。

选择“同意协议”,点击下一步继续。

选择“添加Redis目录到环境变量PATH中”,这样方便系统自动识别Redis执行文件在哪里。

端口号可保持默认的6379,并选择防火墙例外,从而保证外部可以正常访问Redis服务。

设定最大值为100M。作为实验和学习,100M足够了。

点击安装后,正式的安装过程开始。稍等一会即可完成。

4.配置redis登陆密码

安装完毕后,需要先做一些设定工作,以便服务启动后能正常运行。

使用文本编辑器,这里使用Notepad++,打开Redis服务配置文件。

注意:不要找错了,通常为redis.windows-service.conf,而不是redis.windows.conf。后者是以非系统服务方式启动程序使用的配置文件。

找到含有requirepass字样的地方,追加一行,输入requirepass 12345。(注意前面不能有空格

这是访问Redis时所需的密码,一般测试情况下可以不用设定密码。

不过,即使是作为本地访问,也建议设定一个密码。此处以简单的12345来演示。

点击“开始”>右击“计算机”>选择“管理”。在左侧栏中依次找到并点击“计算机管理(本地)”>服务和应用程序>服务。再在右侧找到Redis名称的服务,查看启动情况。如未启动,则手动启动之。

正常情况下,服务应该正常启动并运行了。

5.测试

最后来测试一下Redis是否正常提供服务。

进入Redis的目录,cd C:Program FilesRedis。

输入redis-cli并回车。(redis-cli是客户端程序)

如图正常提示进入,并显示正确端口号,则表示服务已经启动。

使用服务前需要先通过密码验证。

输入“auth 12345”并回车(12345是之前设定的密码)。

返回提示OK表示验证通过。

实际测试一下读写。

输入set mykey1 "I love you all!”并回车,用来保存一个键值。

再输入get mykey1,获取刚才保存的键值。

读取没有问题,表明Redis服务安装成功。

注意事项

  • Windows使用的这个Redis是64位版本的。
  • 作为服务运行的Redis配置文件,通常为redis.windows-service.conf,而不是redis.windows.conf。小心不要选错了。
  • 解压版需要先运行启动 redis-server.exe redis.windows-service.conf 启动redis服务器

三、redis常用配置说明

1、requirepass foobar

给redis设置密码

打开 redis.windows-service.conf配置文件,直接搜索requirepass回车

修改为:

重启服务端后,在客户端使用auth命令,验证密码。

E:Program FilesRedis>redis-cli.exe
127.0.0.1:6379> keys *
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123456

注:不验证密码会出现错误

2、databases 16

Redis默认有16个数据库,寻址角标从0开始。

默认连接db0

客户端使用select命令,切换数据库

127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]>

3、port 6379

指定redis的服务端口,默认6379.

4、loglevel notice(了解)

日志输出级别

debug 记录很多信息,用于开发和测试 varbose 很多精简的有用信息,不像debug会记录那么多 notice 普通的verbose,常用于生产环境 warning 只有非常重要或者严重的信息会记录到日志

5、logfile

Redis日志输出目录,默认不输出日志到文件。

将日志输出目录设置为以下位置:

logfile "D:/redis/log/redis.log"

注意: 文件夹必须存在,redis不会自动创建文件夹 路径格式必须正确

6、网络配置

BIND

127.0.0.1本机回送地址,请求不会经过网络。

如果把bind设置为具体的IP,是危险的,所有的人都可以访问的到,所以默认的设置bind为127.0.0.1,只有本机访问本机。

bind 127.0.0.1

如果要让其他ip连接,则把该行注释掉

#bind 127.0.0.1

protected-mode

protected-mode yes

yes时只能本机访问。也就是127.0.0.1 。保护模式,禁止外网访问,需要外网访问的时候关闭这个东西。(生产环境中一般是一个可信的环境,为什么可信 环境是自己配置的,我们把这个设置为no,因为是一个可信的环境,所以外网是访问不到的。)

注:生产中redis是禁止外网访问的,因为被人访问到就可以操作数据。(但是有时候必须外网访问 就 通过重命名配置)

protected-mode no

timeout

timeout 0

设置客户端连接时的超时时间,单位为秒。当客户端在这段时间内没有发出任何指令,那么关闭该连接

默认值:0代表禁用,永不关闭

timeout 360

7、数据持久化

指定数据持久化的文件名及目录。

# The filename where to dump the DB
dbfilename dump.rdb

# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir ./

dbfilename

缓存文件名字

dir

rdb文件 日志文件 缓存文件目录 默认 . 当前目录

save

数据更新规则 多少seconds(秒) 多少changes(改变数量) 就保存一次。 这些叫做存盘点

save 900 1                #900秒有一个改变的保存一次
save 300 10                #300秒有10个改变就保存一次
save 60 10000              #60秒有10000个改变就保存一次

修改持久化目录重启后,由于持久化文件位置改变,原存储redis的数据也没有了

8.AOF

appendonly

默认情况下,Redis会异步的把数据保存到硬盘。如果你的应用场景允许因为系统崩溃等极端情况而导致最新数据丢失的话,那这种做法已经很ok了。否则你应该打开‘append only’模式,开启这种模式后,Redis会在appendonly.aof文件中添加每一个写操作,这个文件会在Redis启动时被读取来在内存中重新构建数据集。 注意:如果你需要,你可以同时开启‘append only’模式和异步dumps模式(你需要注释掉上面的‘save’表达式来禁#止dumps),这种情况下,Redis重建数据集时会优先使用appendonly.aof而忽略dump.rdb

appendfilename

AOF保存的文件名字。默认appendonly.aof

appendfsync

每一个命令,都立即同步到aof文件中去(很安全,但是速度慢,因为每一个命令都会进行一次磁盘操作) appendfsync always 每秒将数据写一次到aof文件(推荐) appendfsync everysec 将写入工作交给操作系统,由操作系统来判断缓冲区大小,统一写到aof文件(速度快,但是同步频率低,容易丢数据) appendfsync no

四、Redis的键key

1、key的类型

redis的key 值是二进制安全的,这意味着可以用任何二进制序列作为key值,从形如”foo”的简单字符串到一个JPEG文件的内容都可以。

空字符串也是有效key值。

redis建议使用字符串做为key的类型

2、key取值规范

(1)键值不需要太长,消耗内存,在数据中查找这类键值的计算成本较高

(2)键值不宜过短,可读性较差,通常建议见名知意。

3、取值举例

将如下数据库表中的数据,转换为redis的key-value存储

id

username

password

email

1

lisi

111111

lisi@163.com

建议格式:

 obj名:id:id值    
127.0.0.1:6379> set user:id:1:username lisi 
OK 
127.0.0.1:6379> set user:id:1:password 111111
OK 
127.0.0.1:6379> set user:id:1:email lisi@163.com 
OK 
 
127.0.0.1:6379> keys user:id:1* #查找有几个属性
1) "user:id:1:password" 
2) "user:id:1:username" 
3) "user:id:1:email" 
127.0.0.1:6379>

4、Key命令

exists key  
检查给定key是否存在。
del key
删除一个key
 
del key1 key2 key3  删除多个key
 
keys pattern
查找所有符合给定模式 pattern 的 key 。
keys * 匹配数据库中所有 key 。
keys n?me 匹配 name、neme、nfme 等。
keys n* 匹配 name、neme、naaaaame等。
keys n[ae]me 只能匹配 name、neme。
 
expire key seconds
指定key的过期时间。
新添加的key,如果没有指定过期时间,则会一直保存。
可以对一个已经带有生存时间的key执行EXPIRE命令,新指定的生存时间会取代旧的生存时间。
ttl key
查看某个key的剩余过期时间,返回值:
-2 表示这个key已经过期,删除掉
-1 表示没有设置过期时间
其它 表示剩余的生存时间,单位为秒。

rename
语法格式:rename key newkey
将 key 改名为 newkey 。
当 key 和 newkey 相同,或者 key 不存在时,返回一个错误。
当 newkey 已经存在时, RENAME 命令将覆盖旧值。
 
type key
查看key对应的value的数据结构类型。
其它key命令见redis帮助文档   http://doc.redisfans.com/

缓存雪崩 当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。 如何避免? 1):在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。 2):做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期 3):不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

五、Redis的值value(数据结构类型)

Redis的数据结构类型,指的就是redis的值value的类型;

Redis常用的数据结构类型:String、List、Set、SortedSet、hash

1、String类型

​ String类型是redis最常用的数据结构类型,存储的值为字符串。

1.1、String相关命令

set key value
设置一个key,值为value,类型为String类型;如果这个key已经存在,则更新这个key的值。
返回值
1 表示成功
0 表示失败
 
setnx key value
如果这个key不存在,则设置一个key,值为value;如果key存在,则不做更新。
返回值
1 表示成功
0 表示失败
 
get key
获取key对应的value值;如果key不存在,则返回nil
 
 
mget key1 key2 key3
一次获取多个key的值,如果对应key不存在,则对应返回nil。
 
incr key
将 key 中储存的数字值增一,然后返回。
如果这个key不存在,那么key的值会先被初始化为0,然后再执行INCR
操作。
如果这个key对应的value值,不能表示数字,则会返回一个错误。
场景:微博点赞(统计、计数)
incrby key increment
    将key增加指定的步长值。
decr key
将 key 中储存的数字值减一,然后返回。
如果这个key不存在,那么key的值会先被初始化为0,然后再执行INCR操作。
如果这个key对应的value值,不能表示数字,则会返回一个错误。
Redis的key是单线程模式,这就意味一瞬间只有一个线程能够持有这个key,所以可以使用redis解决部分涉及线程安全的业务。
decrby key decrement
将key减少对应的步长值。
 
append key value
如果key已经存在,则将value追加到这个key原先的value值的末尾。
    如果这个key不存在,则执行set操作。
 
127.0.0.1:6379> help @string 查看string类型的帮助文档
127.0.0.1:6379> help set   查看set命令的帮助文档

2、List类型

2.1、特点

(1)基于Linked List实现

(2)元素是字符串类型

(3)列表头尾增删快,中间增删慢,增删元素是常态

(4)元素可以重复出现

(5)最多包含2^32-1元素

列表的索引

n 从左至右,从0开始

n 从右至左,从-1开始

2.2、List类型相关命令

lpush key value [value ...]
将一个或多个值value插入到列表key的表头(即从左边插入);
如果有多个value值,那么各个value值按从左到右的顺序依次插入到表头:比如说,对空列表mylist执行命令LPUSH mylist a b c,列表的值将是 c b a 这等同于原子性地执行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三个命令;
如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。
当 key 存在但不是列表类型时,返回一个错误。
 
rpush key value [value ...]
  尾部添加(从右向左),操作同上。
 
llen key
返回key对应list的长度,key不存在返回0,如果key对应类型不是list返回错误
 
lindex key index
index元素在list列表中的下角标,从0开始;
lindex  是从左到右取元素
lrange key start stop
获取指定区间的所有元素;
下角标从0开始,0表示第一个元素,1表示第二个,依次类推;
-1表示最后一个元素,-2表示倒数第二个元素,依次类推;
lpop key   
移除并返回列表中的第一个元素
rpop key   
移除并返回列表中的最后一个元素。

其他方法:help @list

2.3、List类型应用场景

使用于与统计排名有关的业务或者操作,聊天室功能。

新浪微博抢沙发/评论

3、Hash类型

3.1、特点

由field和与之关联的value组成map键值对,field和value是字符串类型;

一个hash中最多包含2^32-1键值对。

4.3.2、Hash相关命令

hset key field value
设置hash field为指定值,如果key不存在,则先创建
如果field已经存在,那么将更新这个field的值。
 
hget key field  
获取指定的hash field
 
hmget key filed1....fieldN
获取全部指定的hash filed
 
hmset key filed1 value1 ... filedN valueN
同时设置hash的多个field
 
hexists key field
测试指定field是否存在
 
hdel key field
删除指定的hash field
 
hlen key
返回指定hash的field数量
 
hkeys key
返回hash的所有field
 
hvals key
返回hash的所有value
 
hgetall
返回hash的所有filed和value

3.3、Hash的用途

节约内存空间:

redis每创建一个键,都会为这个键储存一些附加的管理信息(比如这个键的类型,这个键最后一次被访问的时间等等)

所以数据库里面的键越多,redis数据库服务器在储存附加管理信息方面耗费的内存就越多,在获取key对应的value值时cpu的开销也会更多

Hash结构可以将具有关联关系的一组key-value,存储到同一个hash结构中,从而减少key的数量。

3.4、hash不适用的场景

需要设置键过期功能的key:

Redis的key的过期功能只能对键操作,而Hash结构不能单独对某一个filed设置过期功能。

4、Set类型(集合)

4.1、特点

无序的、去重的;

元素是字符串类型;

最多包含2^32-1元素。

4.2、Set相关命令

sadd key member [member ...](无序不重复)
将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
假如 key 不存在,则创建一个只包含 member 元素作成员的集合。
当 key 不是集合类型时,返回一个错误。
 
smembers key
返回集合 key 中的所有成员。
不存在的 key 被视为空集合。
 
spop key
移除并返回集合中的一个随机元素。
被移除的随机元素。
当key不存在或key是空集时,返回nil。
 
scard key
返回集合key的基数(集合中元素的数量)。
集合的基数。
当key不存在时,返回0。
 
交集、并集、差集sinter sunion sdiff
sinter key [key ...]
返回一个集合的全部成员,该集合是所有给定集合的交集。
不存在的 key 被视为空集。
 
sunion key [key ...]
返回一个集合的全部成员,该集合是所有给定集合的并集。
不存在的 key 被视为空集。
 
sdiff key [key ...]
返回一个集合的全部成员,该集合是所有给定集合之间的差集。
不存在的 key 被视为空集。

4.3、应用场景

新浪微博的共同关注

需求:当用户访问另一个用户的时候,会显示出两个用户共同关注哪些相同的用户

设计:将每个用户关注的用户放在集合中,求交集即可

实现如下:

peter={‘john’,‘jack’,‘may’}

ben={‘john’,‘jack’,‘tom’}

那么peter和ben的共同关注为:

SINTER peter ben 结果为{‘john’,‘jack’}

5、SortedSet类型

5.1、特点

类似Set集合;

有序的、去重的;

元素是字符串类型;

每一个元素都关联着一个浮点数分值(Score),并按照分值从小到大的顺序排列集合中的元素。分值可以相同

最多包含2^32-1元素

5.2、适用场景

适用于需要有序且唯一的业务或操作:

(1)网易音乐排行榜

分析:

每首歌的歌名作为元素(唯一、不重复)

每首歌的播放次数作为分值

ZREVRANGE来获取播放次数最多的歌曲(就是最多播放榜了,云音乐热歌榜,没有竞价,没有权重)

ZREVRANGE key start stop [WITHSCORES]

返回有序集 key 中,指定区间内的成员。

其中成员的位置按 score 值递减(从大到小)来排列。
具有相同 score 值的成员按字典序的逆序(reverse lexicographical order)排列。

六、Jedis连接redis服务器

1、引入依赖包

<dependency><!--连接驱动-->
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

2、Jedis连接redis服务端:

public class TestJedis {
 
 @Test
    public void testJedisConnect(){
// 连接redis服务器端(使用host)
        @SuppressWarnings("resource")
        Jedis jedis = new Jedis("localhost");
        jedis.auth("123456");//登陆认证
        System.out.println("连接redis服务端成功!");
//测试redis服务器是否正在运行
        System.out.println("redis服务器正在运行吗?"+ jedis.ping());
        System.out.println("redis服务器信息?n"+ jedis.info());
    }
}

注意:

1.防火墙需要放行6379默认端口

2.使用jedis连接redis可能会出现的问题及解决方案:

此时需要根据网络配置部分修改bind和protected-mode

1)ip绑定问题
Connection refused: connect
 
把Redis的配置文件里的
bind localhost(或者bind 127.0.0.1,表明只有该主机才能访问)注释掉。
 
或者修改为:
bind ip     表明,其它机器可以通过ip访问,但是主机不能访问。
 
 
2)、保护模式
DENIED Redis is running in protected mode because protected mode is enabled…
redis处于保护模式,只能本地链接,我们需要修改配置文件redis.conf,将protected-mode yes改成no

3、Key测试

@Test
    public void testKey(){
        @SuppressWarnings("resource")
        Jedis jedis = new Jedis("localhost");
        jedis.auth("123456") ; //如果有密码配置
        System.out.println("连接redis服务端成功!");
//测试redis服务器是否正在运行
        System.out.println("redis服务器正在运行吗?"+ jedis.ping());
        jedis.set("jediskey", "apple");
        String value = jedis.get("jediskey");
        System.out.println(value);
        Set<String> keys = jedis.keys("*");
        for (String string : keys) {
            System.out.println(string);
        }
    }

4、List测试

 @Test
    public void testList(){
        @SuppressWarnings("resource")
        Jedis jedis = new Jedis("localhost");
        jedis.auth("123456") ; //如果有密码配置
        System.out.println("连接redis服务端成功!");
//测试redis服务器是否正在运行
        System.out.println("redis服务器正在运行吗?"+ jedis.ping());
        jedis.lpush("list01", "string1");
        jedis.lpush("list01", "string2");
        jedis.lpush("list01", "string3");
        List<String> listString = jedis.lrange("list01", 0, 2);
        for (String string : listString) {
            System.out.println(string);
        }
    }

5、Set测试

 @Test
    public void testSet(){
        @SuppressWarnings("resource")
        Jedis jedis = new Jedis("localhost");
        jedis.auth("123456") ; //如果有密码配置
        System.out.println("连接redis服务端成功!");
//测试redis服务器是否正在运行
        System.out.println("redis服务器正在运行吗?"+ jedis.ping());
        jedis.sadd("jedisset", "mysql");
        jedis.sadd("jedisset", "oracle","mongodb");
        Set<String> set = jedis.sunion("jedisset");
        for (String string : set) {
            System.out.println(string);
        }
    }

6、Hash测试:

public void testHash(){
        @SuppressWarnings("resource")
        Jedis jedis = new Jedis("localhost");
        jedis.auth("123456") ; //如果有密码配置
        System.out.println("连接redis服务端成功!");
//测试redis服务器是否正在运行
        System.out.println("redis服务器正在运行吗?"+ jedis.ping());
        jedis.hset("hash01", "name", "zhangsan");
        jedis.hset("hash01", "age", "18");
        jedis.hset("hash01", "phone", "18627738874");
        List<String> hashString = jedis.hmget("hash01", "name","age","phone");
        for (String string : hashString) {
            System.out.println(string);
        }
    }

七.spring 整合 redis

1.RedisTemplate处理对象

​ 大多数用户可能使用RedisTemplate以及它的coresponding包org.springframe.data.redis.core。 模板实际上是Redis模块的中心类,因为它有丰富的特性集。模板提供了Redis交互的高级抽象。虽然RedisConnection提供了接受和返回二进制值(字节数组)的低级方法,但是模板负责序列化和连接管理,使用户不必处理这些细节。

   RedisTemplate的大部分操作都使用基于java的序列化器。这意味着模板编写或读取的任何对象都通过Java进行序列化和反序列化。可以在模板上更改序列化机制,Redis模块提供了几个实现,这些实现在org.springframework.data.redis.serializer序列化程序包。您还可以将任何序列化器设置为null,并通过将enableDefaultSerializer属性设置为false,对原始字节数组使用RedisTemplate。注意,模板要求所有键都是非空的。但是,只要底层序列化器接受值,值就可以为空。

RedisTemplate支持

熟悉Spring的JdbcTemplate对象的话,应该大概能猜出来RedisTemplate的作用了,RedisTemplate对象对RedisConnection进行了封装,它提供了连接管理,序列化等功能,它对Redis的交互进行了更高层次的抽象。另外还提供了Redis操作命令的操作视图,这极大的方便和简化了Redis的操作。

下表是具体的操作视图接口类介绍:

Key类型操作

ValueOperations

Redis String/Value 操作

ListOperations

Redis List 操作

SetOperations

Redis Set 操作

ZSetOperations

Redis Sort Set 操作

HashOperations

Redis Hash 操作

Value约束操作

BoundValueOperations

Redis String/Value key 约束

BoundListOperations

Redis List key 约束

BoundSetOperations

Redis Set key 约束

BoundZSetOperations

Redis Sort Set key 约束

BoundHashOperations

Redis Hash key 约束

2.整合

2.1.pom.xml文件中引入Redis依赖

<dependencies>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version><!--注意版本区别,springDataRedis2以下的依赖jedis版本是在3.0以下的-->
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->

        <dependency><!--spring提供的与redis集成的依赖-->
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.8.12.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.8.3</version>
        </dependency>





    </dependencies>

2.2.配置文件配置

#redis
spring.redis.host=localhost
spring.redis.password=123456
spring.redis.port=6379
spring.redis.pool.max-idle=3
spring.redis.pool.max-active=20

2.3 spring配置类配置

@Configuration//spring整合redis配置类
/**
 * spring对redis的操作,都通过RedisTemplate对象来进行:
 * 1.获取Jedis的连接工厂,获取连接信息   获取Jedis的连接池配置
 * 2.设置redis的序列化方式
 * 3.获取RedisTemplate
 */
@PropertySource(encoding = "utf-8",value = "classpath:redis.properties")
public class RedisConfig {
	@Value("${spring.redis.host}")
	public String host;
	@Value("${spring.redis.password}")
	public String password;
	@Value("${spring.redis.port}")
	public int port;
	@Value("${spring.redis.pool.max-idle}")
	public int maxIdle;
	@Value("${spring.redis.pool.max-active}")
	public int maxActive;
 
 
	//1.获取Jedis的连接工厂,获取连接信息   获取Jedis的连接池配置
    @Bean
    public JedisConnectionFactory getConnectionFactory(){
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setHostName(host);//设置主机ip
        jedisConnectionFactory.setPort(port);//设置端口号
        jedisConnectionFactory.setPassword(password);

        //Jedis的连接池配置
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxIdle(maxIdle);
        jedisConnectionFactory.setTimeout(0);//设置断开连接时间
        jedisConnectionFactory.setPoolConfig(config);
        return jedisConnectionFactory;
    }
 
	// 默认用的是用JdkSerializationRedisSerializer进行序列化的
	//设置redis的序列化方式
    //设置将java对象序列化成  redis的持久化存储在硬盘中的数据  的方式
    //设置将redis数据反序列化成 java对象的方式
    @Bean
    public RedisTemplate<String,Object>  getRedisTemplate(JedisConnectionFactory jedisConnectionFactory){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);//设置Jedis连接工厂
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();//创建string类型序列化对象
        //设置value的序列化方式对象
//        Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决 redis的value是一个复杂json对象数据,无法转换成java对象的问题
        //该序列化方式会将转换成json类型的原java对象类型也作为数据,放入序列化中
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();

        //设置key-value的序列化
        redisTemplate.setKeySerializer(stringRedisSerializer);//设置key序列化方式
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);//设置value序列化方式

        //设置hash类型的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);

        //开启默认配置方式
        redisTemplate.setEnableDefaultSerializer(true);
        redisTemplate.setDefaultSerializer(genericJackson2JsonRedisSerializer);
        return redisTemplate;
    }
}

2.4 常用redisTemplate操作

Redis的String数据结构 
set void set(K key, V value);

redisTemplate.opsForValue().set("num","123");
redisTemplate.opsForValue().get("num")  输出结果为123
set void set(K key, V value, long timeout, TimeUnit unit); 

redisTemplate.opsForValue().set("num","123",10, TimeUnit.SECONDS);
redisTemplate.opsForValue().get("num")设置的是10秒失效,十秒之内查询有结果,十秒之后返回为null
set void set(K key, V value, long offset);

覆写(overwrite)给定 key 所储存的字符串值,从偏移量 offset 开始

template.opsForValue().set("key","hello world");
template.opsForValue().set("key","redis", 6);
System.out.println("***************"+template.opsForValue().get("key"));
结果:***************hello redis
get V get(Object key);

template.opsForValue().set("key","hello world");
System.out.println("***************"+template.opsForValue().get("key"));
结果:***************hello world
getAndSet V getAndSet(K key, V value); 

设置键的字符串值并返回其旧值

template.opsForValue().set("getSetTest","test");
System.out.println(template.opsForValue().getAndSet("getSetTest","test2"));
结果:test
append Integer append(K key, String value);

如果key已经存在并且是一个字符串,则该命令将该值追加到字符串的末尾。如果键不存在,则它被创建并设置为空字符串,因此APPEND在这种特殊情况下将类似于SET。

template.opsForValue().append("test","Hello");
System.out.println(template.opsForValue().get("test"));
template.opsForValue().append("test","world");
System.out.println(template.opsForValue().get("test"));
Hello
Helloworld
size Long size(K key);
返回key所对应的value值得长度

template.opsForValue().set("key","hello world");
System.out.println("***************"+template.opsForValue().size("key"));
***************11
Redis的List数据结构
Long size(K key);
返回存储在键中的列表的长度。如果键不存在,则将其解释为空列表,并返回0。当key存储的值不是列表时返回错误。

System.out.println(template.opsForList().size("list"));
6
Long leftPush(K key, V value);
将所有指定的值插入存储在键的列表的头部。如果键不存在,则在执行推送操作之前将其创建为空列表。(从左边插入)

template.opsForList().leftPush("list","java");
template.opsForList().leftPush("list","python");
template.opsForList().leftPush("list","c++");
返回的结果为推送操作后的列表的长度
1
2
3
Long leftPushAll(K key, V... values);
批量把一个数组插入到列表中

String[] strs = new String[]{"1","2","3"};
template.opsForList().leftPushAll("list",strs);
System.out.println(template.opsForList().range("list",0,-1));
[3, 2, 1]
Long rightPush(K key, V value);
将所有指定的值插入存储在键的列表的头部。如果键不存在,则在执行推送操作之前将其创建为空列表。(从右边插入)

template.opsForList().rightPush("listRight","java");
template.opsForList().rightPush("listRight","python");
template.opsForList().rightPush("listRight","c++");
1
2
3
Long rightPushAll(K key, V... values);

String[] strs = new String[]{"1","2","3"};
template.opsForList().rightPushAll("list",strs);
System.out.println(template.opsForList().range("list",0,-1));
[1, 2, 3]
void set(K key, long index, V value);
在列表中index的位置设置value值

System.out.println(template.opsForList().range("listRight",0,-1));
template.opsForList().set("listRight",1,"setValue");
System.out.println(template.opsForList().range("listRight",0,-1));
[java, python, oc, c++]
[java, setValue, oc, c++]
Long remove(K key, long count, Object value);
从存储在键中的列表中删除等于值的元素的第一个计数事件。
计数参数以下列方式影响操作:
count> 0:删除等于从头到尾移动的值的元素。
count <0:删除等于从尾到头移动的值的元素。
count = 0:删除等于value的所有元素。

System.out.println(template.opsForList().range("listRight",0,-1));
template.opsForList().remove("listRight",1,"setValue");//将删除列表中存储的列表中第一次次出现的“setValue”。
System.out.println(template.opsForList().range("listRight",0,-1));
[java, setValue, oc, c++]
[java, oc, c++]
V index(K key, long index);
根据下表获取列表中的值,下标是从0开始的

System.out.println(template.opsForList().range("listRight",0,-1));
System.out.println(template.opsForList().index("listRight",2));
[java, oc, c++]
c++
V leftPop(K key);
弹出最左边的元素,弹出之后该值在列表中将不复存在

System.out.println(template.opsForList().range("list",0,-1));
System.out.println(template.opsForList().leftPop("list"));
System.out.println(template.opsForList().range("list",0,-1));
[c++, python, oc, java, c#, c#]
c++
[python, oc, java, c#, c#]
V rightPop(K key);
弹出最右边的元素,弹出之后该值在列表中将不复存在

System.out.println(template.opsForList().range("list",0,-1));
System.out.println(template.opsForList().rightPop("list"));
System.out.println(template.opsForList().range("list",0,-1));
[python, oc, java, c#, c#]
c#
[python, oc, java, c#]
Redis的Hash数据机构
Long delete(H key, Object... hashKeys);
删除给定的哈希hashKeys

System.out.println(template.opsForHash().delete("redisHash","name"));
System.out.println(template.opsForHash().entries("redisHash"));
1
{class=6, age=28.1}
Boolean hasKey(H key, Object hashKey);
确定哈希hashKey是否存在

System.out.println(template.opsForHash().hasKey("redisHash","666"));
System.out.println(template.opsForHash().hasKey("redisHash","777"));
true
false
HV get(H key, Object hashKey);
从键中的哈希获取给定hashKey的值

System.out.println(template.opsForHash().get("redisHash","age"));
26
 Set<HK> keys(H key);
获取key所对应的散列表的key

System.out.println(template.opsForHash().keys("redisHash"));
//redisHash所对应的散列表为{class=1, name=666, age=27}
[name, class, age]
Long size(H key);
获取key所对应的散列表的大小个数

System.out.println(template.opsForHash().size("redisHash"));
//redisHash所对应的散列表为{class=1, name=666, age=27}
3
 void putAll(H key, Map<? extends HK, ? extends HV> m);
使用m中提供的多个散列字段设置到key对应的散列表中

Map<String,Object> testMap = new HashMap();
testMap.put("name","666");
testMap.put("age",27);
testMap.put("class","1");
template.opsForHash().putAll("redisHash1",testMap);
System.out.println(template.opsForHash().entries("redisHash1"));
{class=1, name=jack, age=27}
 void put(H key, HK hashKey, HV value);
设置散列hashKey的值

template.opsForHash().put("redisHash","name","666");
template.opsForHash().put("redisHash","age",26);
template.opsForHash().put("redisHash","class","6");
System.out.println(template.opsForHash().entries("redisHash"));
{age=26, class=6, name=666}
 List<HV> values(H key);
获取整个哈希存储的值根据密钥

System.out.println(template.opsForHash().values("redisHash"));
[tom, 26, 6]
 Map<HK, HV> entries(H key);
获取整个哈希存储根据密钥

System.out.println(template.opsForHash().entries("redisHash"));
{age=26, class=6, name=tom}
 Cursor<Map.Entry<HK, HV>> scan(H key, ScanOptions options);
使用Cursor在key的hash中迭代,相当于迭代器。

Cursor<Map.Entry<Object, Object>> curosr = template.opsForHash().scan("redisHash", 
  ScanOptions.ScanOptions.NONE);
    while(curosr.hasNext()){
        Map.Entry<Object, Object> entry = curosr.next();
        System.out.println(entry.getKey()+":"+entry.getValue());
    }
age:27
class:6
name:666
Redis的Set数据结构
Long add(K key, V... values);
无序集合中添加元素,返回添加个数
也可以直接在add里面添加多个值 如:template.opsForSet().add("setTest","aaa","bbb")

String[] strs= new String[]{"str1","str2"};
System.out.println(template.opsForSet().add("setTest", strs));
2
 Long remove(K key, Object... values);
移除集合中一个或多个成员

String[] strs = new String[]{"str1","str2"};
System.out.println(template.opsForSet().remove("setTest",strs));
2
 V pop(K key);
移除并返回集合中的一个随机元素

System.out.println(template.opsForSet().pop("setTest"));
System.out.println(template.opsForSet().members("setTest"));
bbb
[aaa, ccc]
 Boolean move(K key, V value, K destKey);
将 member 元素从 source 集合移动到 destination 集合

template.opsForSet().move("setTest","aaa","setTest2");
System.out.println(template.opsForSet().members("setTest"));
System.out.println(template.opsForSet().members("setTest2"));
[ccc]
[aaa]
 Long size(K key);
无序集合的大小长度

System.out.println(template.opsForSet().size("setTest"));
1
Set<V> members(K key);
返回集合中的所有成员

System.out.println(template.opsForSet().members("setTest"));
[ddd, bbb, aaa, ccc]
 Cursor<V> scan(K key, ScanOptions options);
遍历set

Cursor<Object> curosr = template.opsForSet().scan("setTest", ScanOptions.NONE);
  while(curosr.hasNext()){
     System.out.println(curosr.next());
  }
ddd
bbb
aaa
ccc
Redis的ZSet数据结构 
Boolean add(K key, V value, double score);
新增一个有序集合,存在的话为false,不存在的话为true

System.out.println(template.opsForZSet().add("zset1","zset-1",1.0));
true
 Long add(K key, Set<TypedTuple<V>> tuples);
新增一个有序集合

ZSetOperations.TypedTuple<Object> objectTypedTuple1 = new DefaultTypedTuple<>("zset-5",9.6);
ZSetOperations.TypedTuple<Object> objectTypedTuple2 = new DefaultTypedTuple<>("zset-6",9.9);
Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<ZSetOperations.TypedTuple<Object>>();
tuples.add(objectTypedTuple1);
tuples.add(objectTypedTuple2);
System.out.println(template.opsForZSet().add("zset1",tuples));
System.out.println(template.opsForZSet().range("zset1",0,-1));
[zset-1, zset-2, zset-3, zset-4, zset-5, zset-6]
Long remove(K key, Object... values);
从有序集合中移除一个或者多个元素

System.out.println(template.opsForZSet().range("zset1",0,-1));
System.out.println(template.opsForZSet().remove("zset1","zset-6"));
System.out.println(template.opsForZSet().range("zset1",0,-1));
[zset-1, zset-2, zset-3, zset-4, zset-5, zset-6]
1
[zset-1, zset-2, zset-3, zset-4, zset-5]
 Long rank(K key, Object o);
返回有序集中指定成员的排名,其中有序集成员按分数值递增(从小到大)顺序排列

System.out.println(template.opsForZSet().range("zset1",0,-1));
System.out.println(template.opsForZSet().rank("zset1","zset-2"));
[zset-2, zset-1, zset-3, zset-4, zset-5]
0   //表明排名第一
Set<V> range(K key, long start, long end);
通过索引区间返回有序集合成指定区间内的成员,其中有序集成员按分数值递增(从小到大)顺序排列

System.out.println(template.opsForZSet().range("zset1",0,-1));
[zset-2, zset-1, zset-3, zset-4, zset-5]
Long count(K key, double min, double max);
通过分数返回有序集合指定区间内的成员个数

System.out.println(template.opsForZSet().rangeByScore("zset1",0,5));
System.out.println(template.opsForZSet().count("zset1",0,5));
[zset-2, zset-1, zset-3]
3
Long size(K key);
获取有序集合的成员数,内部调用的就是zCard方法

System.out.println(template.opsForZSet().size("zset1"));
6
 Double score(K key, Object o);
获取指定成员的score值

System.out.println(template.opsForZSet().score("zset1","zset-1"));
2.2
Long removeRange(K key, long start, long end);
移除指定索引位置的成员,其中有序集成员按分数值递增(从小到大)顺序排列

System.out.println(template.opsForZSet().range("zset2",0,-1));
System.out.println(template.opsForZSet().removeRange("zset2",1,2));
System.out.println(template.opsForZSet().range("zset2",0,-1));
[zset-1, zset-2, zset-3, zset-4]
2
[zset-1, zset-4]
Cursor<TypedTuple<V>> scan(K key, ScanOptions options);
遍历zset

Cursor<ZSetOperations.TypedTuple<Object>> cursor = template.opsForZSet().scan("zzset1", ScanOptions.NONE);
    while (cursor.hasNext()){
       ZSetOperations.TypedTuple<Object> item = cursor.next();
       System.out.println(item.getValue() + ":" + item.getScore());
    }
zset-1:1.0
zset-2:2.0
zset-3:3.0
zset-4:6.0