springboot-redis
1Nosql定义和分类
1.1什么是NoSql
- NoSql,叫非关系型数据库,它的全名Not only sql。
- 为了解决高并发、高可用、高可扩展,大数据存储等一系列问题而产生的数据库解决方案,就是NoSql。它不能替代关系型数据库,只能作为关系型数据库的一个良好补充。
1.2NoSql的分类
1.2.1键值(Key-Value)存储数据库
相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
典型应用: 内容缓存,主要用于处理大量数据的高访问负载。
数据模型: 一系列键值对
优势: 快速查询
劣势: 存储的数据缺少结构化
1.2.2列存储数据库
相关产品:Cassandra, HBase, Riak
典型应用:分布式的文件系统
数据模型:以列簇式存储,将同一列数据存在一起
优势:查找速度快,可扩展性强,更容易进行分布式扩展
劣势:功能相对局限
1.2.3文档型数据库
相关产品:CouchDB、MongoDB
典型应用:Web应用(与Key-Value类似,Value是结构化的)
数据模型: 一系列键值对
优势:数据结构要求不严格
劣势: 查询性能不高,而且缺乏统一的查询语法
1.2.4图形(Graph)数据库
相关数据库:Neo4J、InfoGrid、Infinite Graph
典型应用:社交网络
数据模型:图结构
优势:利用图结构相关算法。
劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。
2Redis定义,历史和应用场景
2.1什么是redis
Redis是使用c语言开发的一个高性能键值数据库。Redis可以通过一些键值类型来存储数据。
键值类型:
String字符类型
map散列类型hash
list列表类型
set集合类型
sortedset有序集合类型
2.2redis历史发展
2008年,意大利的一家创业公司Merzia推出了一款基于MySQL的网站实时统计系统LLOOGG,然而没过多久该公司的创始人 Salvatore Sanfilippo便 对MySQL的性能感到失望,于是他决定亲自为LLOOGG量身定做一个数据库,并于2009年开发完成,这个数据库就是Redis。 不过Salvatore Sanfilippo并不满足只将Redis用于LLOOGG这一款产品,而是希望更多的人使用它,于是在同一年Salvatore Sanfilippo将Redis开源发布,并开始和Redis的另一名主要的代码贡献者Pieter Noordhuis一起继续着Redis的开发,直到今天。
Salvatore Sanfilippo自己也没有想到,短短的几年时间,Redis就拥有了庞大的用户群体。Hacker News在2012年发布了一份数据库的使用情况调查,结果显示有近12%的公司在使用Redis。国内如新浪微博、街旁网、知乎网,国外如GitHub、Stack Overflow、Flickr等都是Redis的用户。
VMware公司从2010年开始赞助Redis的开发, Salvatore Sanfilippo和Pieter Noordhuis也分别在3月和5月加入VMware,全职开发Redis。
2.3redis的应用场景
缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用)
分布式集群架构中的session分离。
聊天室的在线好友列表。
任务队列。(秒杀、抢购、12306等等)
应用排行榜。
网站访问统计。
数据过期处理(可以精确到毫秒)
3Redis下载和安装
3.1windows版下载和安装
window版下载地址:https://github.com/MicrosoftArchive/redis/releases
下载Redis For window X64.zip,解压到指定目录
3.1.1Redis的目录结构
Redis.windows.conf:redis的配置文件。文件内可以声明redis的端口号,database的数量,密码,是否后台运行,持久化机制,日志等信息。
Redis-server.exe:redis服务端,可以启动redis服务。
Redis-cli.exe: redis自带客户端,启动以后即可连接服务端,通过以命令的形式向redis服务中存取数据,也可以关闭redis服务。
3.1.2启动redis服务端
双击 redis-server.exe ,启动成功,出现如下界面。端口号6379,pid:1236,如果关闭当前窗口,相当于关闭redis服务。
或者以命令行的方式启动redis,在当前目录下输入redis-server即可启动redis服务(配置要写)
3.1.3启动redis客户端
双击redis-cli.exe即可启动客户端:如下图表示,则已经连上服务端,可以通过命令存取数据了。
如果服务端设置了密码,在运行客户端连接服务端时,需要加上密码
./redis-cli -a 123456 -------123456为密码
3.1.4图形化界面客户端
RedisDesktopManager以图形化界面的方式操作redis中的数据。安装redis桌面管理工具。
建立连接:
输入名称,主机ip,默认端口号6379,即可连接redis服务器。
可以测试连接,如果连接成功,会有如下弹框:
Redis默认有16个数据库,如果不指定,默认使用database0
向第一个数据库中添加,修改,删除数据。
3.1.5指定配置启动redis服务
Redis启动时可以不指定配置文件,会使用默认配置启动。也可以指定配置文件。如果我们修改了配置文件的内容,希望指定以某个配置文件启动,则使用如下方法启动
复制配置文件redis.windows.conf,改名为redis.conf,修改使用密码123456登录。
以该配置文件运行redis服务:
此时,再通过图形化客户端连接服务时,必须指定密码才可以,否则会连接失败。
3.2Linux版下载和安装
3.2.1下载
官网:https://redis.io/download,选择稳定版本。
3.2.2安装
1.解压
通过xftp把redis安装包上传到指定文件夹。解压:
tar -zxvf redis-6.0.6.tar.gz
2.安装
由于redis依赖c语言环境,所以先安装gcc
[root@localhost ~]# yum install gcc
如果是线上下载安装包的话,先进入目录 usr/local,下载安装包
[root@localhost local]# wget http://download.redis.io/releases/redis-6.0.6.tar.gz
解压
[root@localhost local]# tar -xvf redis-6.0.6.tar.gz
安装好了c语言环境以后,需要编译redis,进入解压后的目录
[root@localhost local]# cd /usr/local/redis-6.0.6/
编译
[root@localhost local]# make
安装,如果不指明安装路径,就会在当前路径下安装,指明安装路径,会在指明的路径下生成bin文件夹,bin里面是redis的命令
[root@localhost redis-6.0.1]# make PREFIX=/usr/local/redis-6.0.6 install
进入bin
注意:如果在编译的时候报错如下:等等,太长了,不列举了。
make[1]: *** [server.o] 错误 1
make[1]: 离开目录“/usr/redis-6.0.6/src”
make: *** [all] 错误 2
server.c:2402:11: 错误:‘struct redisServer’没有名为‘assert_file’的成员
server.assert_file = "<no file>";
^
server.c:2403:11: 错误:‘struct redisServer’没有名为‘assert_line’的成员
server.assert_line = 0;
^
server.c:2404:11: 错误:‘struct redisServer’没有名为‘bug_report_start’的成员
server.bug_report_start = 0;
^
解决办法:升级gcc版本
[root@localhost redis-6.0.6]# gcc -v # 查看gcc版本
[root@localhost redis-6.0.6]# yum -y install centos-release-scl # 升级到9.1版本
[root@localhost redis-6.0.6]# yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
[root@localhost redis-6.0.6]# scl enable devtoolset-9 bash
以上为临时启用,如果要长期使用gcc 9.1的话:
[root@localhost redis-6.0.6]# echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile
在执行编译就没有问题了,安装成功,会提示:
执行make test,可能会出现如下错误:
[root@localhost redis-6.0.6]# make test
cd src && make test
make[1]: 进入目录“/usr/redis-6.0.6/src”
CC Makefile.dep
make[1]: 离开目录“/usr/redis-6.0.6/src”
make[1]: 进入目录“/usr/redis-6.0.6/src”
You need tcl 8.5 or newer in order to run the Redis test
make[1]: *** [test] 错误 1
make[1]: 离开目录“/usr/redis-6.0.6/src”
make: *** [test] 错误 2
解决办法:
[root@localhost redis-6.0.6]# yum install tcl
[root@localhost redis-6.0.6]# make test
重新测试成功。
3.2.3运行redis
以某个配置文件启动redis服务,一定要注意配置文件的路径
3.2.4后台启动
上面redis的启动方式,是前端启动,一关闭客户端,redis的服务也就停掉了,所以这种启动方式非常不友好。我们可以修改配置文件中的启动方式:
[root@localhost redis-6.0.6]# vim redis.conf
进到redis.conf文件里,然后找到daemonize no把no改为yes
启动的时候指定修改之后的配置文件即可。
3.2.5查看是否启动成功
3.2.6关闭redis服务
或者:
如果redis设置了密码,则以下面方式关闭,123456为密码
3.2.7使用桌面管理工具连接Linux redis
修改redis配置文件,注释掉bind 127.0.0.1
保护模式关闭:
设置密码:
3.2.8以服务方式启动redis
- 把redis加入service服务
vim /lib/systemd/system/redis.service
- 写入
[Unit]
Description=redis
After=network.target
[Service]
Type=forking
PIDFile=/var/run/redis_6379.pid
ExecStart=/usr/local/redis-6.0.6/src/redis-server /usr/local/redis-6.0.6/etc/redis.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
注意路径问题,以及PIDFile的路径对应的是redis-6.0.6/redis.conf里面的PIDFile
保存,退出。
- 运行以下
[root@localhost redis-6.0.6]# systemctl enable redis.service # 加入开机启动
[root@localhost redis-6.0.6]# systemctl is-enabled redis.service # 查看开机是否启动成功
enabled
[root@localhost redis-6.0.6]# systemctl start redis #开启redis服务
[root@localhost redis-6.0.6]# systemctl status redis //查看redis运行状态
3.3清缓存命令
flushall
把缓存键值对清空。
4Redis的String数据类型的赋值取值删除
4.1命令
4.1.1赋值
语法:SET key value
4.1.2取值
语法:GET key
4.1.3取值并赋值
语法:GETSET key value
4.1.4设置/获取多个键值
语法:
MSET key value [key value …]
MGET key [key …]
4.1.5删除
语法:DEL key
4.1.6数值增减
- 递增数字
当存储的字符串是整数时,Redis提供了一个实用的命令INCR(increate),其作用是让当前键值递增,并返回递增后的值。
语法:INCR key
- 增加指定的整数
语法:INCRBY key increment
- 递减数值
语法:DECR key
- 减少指定的整数
语法:DECRBY key decrement
5Redis的Hash数据类型的赋值取值删除
散列类型
5.1使用string的问题
假设有User对象以JSON序列化的形式存储到Redis中,User对象有id,username、password、age、name等属性,存储的过程如下:
保存、更新:
User对象➡️json(string)➡️redis
如果在业务上只是更新age属性,其他的属性并不做更新我应该怎么做呢? 如果仍然采用上边的方法在传输、处理时会造成资源浪费,下边讲的hash可以很好的解决这个问题。
5.2redis hash介绍
hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:
5.3命令
5.3.1赋值
HSET命令不区分插入和更新操作,当执行插入操作时HSET命令返回1,当执行更新操作时返回0。
- 一次只能设置一个字段值
语法:HSET key field value
- 一次可以设置多个字段值
语法:HMSET key field value [field value ...]
- 当字段不存在时赋值,类似HSET,区别在于如果字段存在,该命令不执行任何操作
语法:HSETNX key field value
5.3.2取值
- 一次只能获取一个字段值
语法:HGET key field
- 一次可以获取多个字段值
语法:HMGET key field [field ...]
- 获取所有字段值
语法:HGETALL key
5.3.3删除字段
可以删除一个或多个字段,返回值是被删除的字段个数
语法:HDEL key field [field ...]
5.3.4增加数字
语法:HINCRBY key field increment
例:将用户的年龄加1
6Redis的List数据类型的赋值取值删除
Redis的list是采用来链表来存储的,所以对于redis的list数据类型的操作,是操作list的两端数据来操作的。
6.2命令
6.2.1向列表两端增加元素
- 向列表左边增加元素
语法:LPUSH key value [value ...]
- 向列表右边增加元素
语法:RPUSH key value [value ...]
6.2.2查看列表
LRANGE命令是列表类型最常用的命令之一,获取列表中的某一片段,将返回start、stop之间的所有元素(包含两端的元素),索引从0开始。索引可以是负数。
“-1”代表最后边的一个元素。
语法:LRANGE key start stop
6.2.3从列表两端弹出元素
LPOP命令从列表左边弹出一个元素,会分两步完成:
第一步是将列表左边的元素从列表中移除
第二步是返回被移除的元素值。
语法:
LPOP key
RPOP key
6.2.4获取列表中元素的个数
语法:LLEN key
7Redis的Set数据类型的赋值取值删除
集合类型
集合类型:无序、不可重复
列表类型:有序、可重复
7.1命令
7.1.1增加/删除元素
语法:SADD key member [member ...]
语法:SREM key member [member ...]
7.1.2获得集合中的所有元素
语法:SMEMBERS key
(无序、不可重复)
7.1.3判断元素是否在集合中
语法:SISMEMBER key member
7.2运算命令
7.2.1集合的差集运算 A-B
属于A并且不属于B的元素构成的集合。
语法:SDIFF key [key ...]
7.2.2集合的交集运算 A ∩ B
属于A且属于B的元素构成的集合。
语法:SINTER key [key ...]
7.2.3集合的并集运算 A ∪ B
属于A或者属于B的元素构成的集合
语法:SUNION key [key ...]
8Redis的SortedSet数据类型的赋值取值删除
8.1Sortedset
- Sortedset又叫zset
- Sortedset是有序集合,可排序的,但是唯一。
- Sortedset和set的不同之处,是会给set中的元素添加一个分数,然后通过这个分数进行排序。
8.2命令
8.2.1增加元素
- 向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。
- 返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。
语法:ZADD key score member [score member ...]
8.2.2获取元素的分数
语法:ZSCORE key member
8.2.3删除元素
- 移除有序集key中的一个或多个成员,不存在的成员将被忽略。
- 当key存在但不是有序集类型时,返回一个错误。
语法:ZREM key member [member ...]
8.2.4获得排名在某个范围的元素列表
- 获得排名在某个范围的元素列表
- 按照元素分数从小到大的顺序返回索引从start到stop之间的所有元素(包含两端的元素)
语法:ZRANGE key start stop [WITHSCORES]
- 按照元素分数从大到小的顺序返回索引从start到stop之间的所有元素(包含两端的元素)
语法:ZREVRANGE key start stop [WITHSCORES]
- 如果需要获得元素的分数的可以在命令尾部加上WITHSCORES参数
8.2.5获取元素的排名
- 从小到大
语法:ZRANK key member
- 从大到小
语法:ZREVRANK key member
9Keys命令
9.1常用命令
9.1.1keys
返回满足给定pattern 的所有key
9.1.2exists
确认一个key 是否存在
示例:从结果来看,数据库中不存在listA这个key,但是setA这个key 是存在的
9.1.3del
删除一个key
9.1.4rename
重命名key
示例:setA成功的被我们改名为setA_new 了
9.1.5type
返回值的类型
示例:这个方法可以非常简单的判断出值的类型
9.1.6设置key的生存时间
Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的
即:到期后数据销毁。
EXPIRE key seconds 设置key的生存时间(单位:秒)key在多少秒后会自动删除 TTL key 查看key剩余的生存时间 PERSIST key 清除生存时间 -1 PEXPIRE key milliseconds 生存时间设置单位为:毫秒 |
- -1:清除生存时间,redis一直存在该key
- -2:到期,redis没有该key
- 正整数:剩余的生存时间
例子:
10SpringBoot整合Redis
10.1新建SpringBoot工程,引入Web,Redis的场景启动器
(1)方式一:创建项目时,勾选
(2)方式二:创建项目后,自行加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
如果网络不稳定,不能下载依赖,删掉下面的文件夹,再试试
10.2application.yml配置redis地址
spring.redis.host
#配置redis地址ip和密码
spring:
redis:
host: 127.0.0.1
password: 123456
10.3在使用处注入StringRedisTemplate或者RedisTemplate
- 有依赖后,springboot自动装配
- 一般在spring.factories里,2.7.1版本在别的位置
- StringRedisTemplate可直接通过@Autowired根据类型进行自动装配
10.3.1StringRedisTemplate处理字符串相关操作
10.3.1.1 Redis的String数据类型
package com.tjetc;
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.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
class ApplicationTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
public void testString() {
System.out.println(stringRedisTemplate);
ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
opsForValue.set("k1", "v1");
opsForValue.set("k2", "v2");
String v1 = opsForValue.get("k1");
System.out.println(v1);
//存储多对key和value
Map<String, String> map = new HashMap<>();
map.put("k3", "v3");
map.put("k4", "v4");
opsForValue.multiSet(map);
}
}
10.3.1.2 Redis的Hash数据类型的赋值取值
@Test
public void testHash() {
//HashOperations操作hash类型的数据
HashOperations<String, Object, Object> opsForHash = stringRedisTemplate.opsForHash();
//存储单个字段名和字段值
opsForHash.put("user","username","kelly");
opsForHash.put("user","age","21");
//存储过程字段名和字段值
Map<String, String> map = new HashMap<>();
map.put("sex","female");
map.put("email","yj@163.com");
opsForHash.putAll("user",map);
//获取单个值
Object o = opsForHash.get("user", "username");
System.out.println("username="+o);
//一次获取多个值
List<Object> hashKeys = Arrays.asList("username", "age", "sex", "email");
List<Object> list = opsForHash.multiGet("user", hashKeys);
System.out.println("user的多个hash值"+list);
}
10.3.1.3 Redis的List数据类型的赋值取值
@Test
public void testList() {
ListOperations<String, String> opsForList = stringRedisTemplate.opsForList();
//向列表左边增加元素
opsForList.leftPush("list", "1");
opsForList.leftPush("list", "2");
//向列表左边增加多个元素
opsForList.leftPushAll("list", "3", "4");
//向列表右边增加元素
opsForList.rightPush("list", "5");
opsForList.rightPush("list", "6");
//向列表右边增加多个元素
opsForList.rightPushAll("list", "7", "8");
//查看列表
List<String> list = opsForList.range("list", 0, -1);
System.out.println(list);
//从列表右边弹出一个元素
String rightLast = opsForList.rightPop("list");
System.out.println("从列表右边弹出一个元素:" + rightLast);
//再次查看列表
List<String> list1 = opsForList.range("list", 0, -1);
System.out.println(list1);
}
再次运行方法,重复添加
10.3.1.4 Redis的Set数据类型的赋值取值
@Test
public void testSet() {
SetOperations<String, String> opsForSet = stringRedisTemplate.opsForSet();
//增加元素
opsForSet.add("setA", "a", "b", "c", "a");
Set<String> setA = opsForSet.members("setA");
System.out.println("setA:" + setA);
opsForSet.add("setB", "b", "c", "d", "b");
Set<String> setB = opsForSet.members("setB");
System.out.println("setB:" + setB);
//差集
Set<String> differenceA = opsForSet.difference("setA", "setB");
System.out.println("属于A并且不属于B的元素构成的集合:" + differenceA);
Set<String> differenceB = opsForSet.difference("setB", "setA");
System.out.println("属于B并且不属于A的元素构成的集合:" + differenceB);
//交集
Set<String> intersect = opsForSet.intersect("setA", "setB");
System.out.println("属于A且属于B的元素构成的集合:" + intersect);
//并集
Set<String> union = opsForSet.union("setA", "setB");
System.out.println("属于A或者属于B的元素构成的集合:" + union);
}
10.3.1.5 Redis的SortedSet(ZSet)数据类型的赋值取值
@Test
public void testZSet() {
ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet();
//增加单个元素
opsForZSet.add("score", "kelly", 100);
//增加多个元素
Set<ZSetOperations.TypedTuple<String>> set = new HashSet<>();
DefaultTypedTuple<String> typedTupleM = new DefaultTypedTuple("marry", (double) 90);
DefaultTypedTuple<String> typedTupleJ = new DefaultTypedTuple("jack", (double) 80);
set.add(typedTupleM);
set.add(typedTupleJ);
opsForZSet.add("score", set);
//获取元素的分数
Set<ZSetOperations.TypedTuple<String>> score = opsForZSet.rangeWithScores("score", 0, -1);
System.out.println(score);
}
10.3.2RedisTemplate
处理对象相关操作
10.3.2.1redisTemplate默认如果保存对象,使用jdk序列化机制
package com.tjetc.entity;
import java.io.Serializable;
/**
* 自定义实体类,目的是要序列化到Redis中
*/
public class User implements Serializable {
private Long id;
private String username;
private String password;
public Long getId() {return id; }
public void setId(Long id) { this.id = id;}
public String getUsername() {return username;}
public void setUsername(String username) {this.username = username;}
public String getPassword() {return password; }
public void setPassword(String password) {this.password = password;}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
package com.tjetc;
import com.tjetc.entity.User;
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.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
@SpringBootTest
public class RedisTemplateTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test() {
//创建一个user对系
User user = new User();
user.setId(1L);
user.setUsername("kelly");
user.setPassword("9588");
/*ValueOperations opsForValue = redisTemplate.opsForValue();
opsForValue.set("u", user);
User user1 = (User)opsForValue.get("u");*/
ValueOperations<Object, User> opsForValue = redisTemplate.opsForValue();
//对象序列化二进制数据后存储到redis中
opsForValue.set("u", user);
//获取user
User user1 = opsForValue.get("u");
System.out.println(user1);
}
}
10.3.2.2改变jdk的序列化规则
①在配置类中配置自定义序列化规则
package com.tjetc.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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
//创建RedisTemplate对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
//设置连接工厂
template.setConnectionFactory(factory);
//创建GenericJackson2JsonRedisSerializer对象,序列化为json格式
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
//template对象设置value的序列化方式
template.setValueSerializer(serializer);
//template对象设置key的序列化方式
template.setKeySerializer(new StringRedisSerializer());
return template;
}
}
②使用
package com.tjetc;
import com.tjetc.entity.User;
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.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
@SpringBootTest
public class RedisConfigurationTemplateTests {
@Autowired
private RedisTemplate<String, User> redisTemplate;
@Test
public void test() {
//创建一个user对系
User user = new User();
user.setId(1L);
user.setUsername("kelly");
user.setPassword("9588");
ValueOperations<String, User> opsForValue = redisTemplate.opsForValue();
//对象序列化二进制数据后存储到redis中
opsForValue.set("u", user);
//获取user
User user1 = opsForValue.get("u");
System.out.println(user1);
}
}
11持久化
11.1Redis持久化概述
持久化的功能:Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。
Redis持久化分为RDB持久化和AOF持久化:前者将当前数据保存到硬盘,后者则是将每次执行的写命令保存到硬盘(类似于MySQL的binlog);由于AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此AOF是目前主流的持久化方式,不过RDB持久化仍然有其用武之地。
下面依次介绍RDB持久化和AOF持久化;由于Redis各个版本之间存在差异,如无特殊说明,以Redis3.0为准。
11.2RDB持久化
# 转储数据库的文件名
默认方式,不需要进行配置,默认就使用这种机制。
在一定的间隔时间中,检测key的变化情况,然后持久化数据
1.编辑redis.windows.conf配置文件
# after 900 sec (15 min) if at least 1 key changed # 900 秒(15 分钟)后,如果至少 1 个键更改 save 900 1 |
# after 300 sec (5 min) if at least 10 keys changed # 300 秒(5 分钟)后,如果至少有 10 个键被更改 save 300 10 |
# after 60 sec if at least 10000 keys changed # 如果至少有 10000 个键被更改,则在 60 秒后 save 60 10000 |
如果我们将配置该成如下形式:
save 900 1 save 300 10 save 10 5:在10秒之后,如果有5个键发生了改变,则执行持久化 |
2.重启服务器,并指定配置文件
- C:\Users\hzw\Documents\Java\software\Redis-x64-3.0.503>redis-server.exe redis.windows.conf
3.启动客户端
127.0.0.1:6379> set username 1 OK 127.0.0.1:6379> set username 2 OK 127.0.0.1:6379> set username 3 OK 127.0.0.1:6379> set username 4 OK 127.0.0.1:6379> set username 5 OK |
上述操作在10秒内完成。这时在当前目录下会出现一个rdb文件。
关闭服务器和客户端。
4.重新启动服务器和客户端,不需要指定配置文件。在客户端直接获取username,结果如下:
127.0.0.1:6379> get username
"5"
11.3AOF持久化
日志记录的方式,可以记录每一条命令的操作。可以每一次命令操作后,持久化数据。
1.编辑redis.windows.conf配置文件
appendonly no(关闭aof) --> appendonly yes (开启aof)
AOF相关配置:
# appendfsync always 每一次操作都进行持久化
appendfsync everysec 每一秒进行一次持久化,默认方式
# appendfsync no 不进行持久化
2.重启服务器,指定配置文件
3.启动客户端,1秒持久化一次
4.关闭并重启服务器(指定配置文件)和客户端,在客户端获取,结果如下:
Redis为什么这么快?
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路 I/O 复用模型,非阻塞 IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;