目录
10.1新建SpringBoot工程,引入Web,Redis的场景启动器
10.3在使用处注入StringRedisTemplate或者RedisTemplate
10.3.2.1redisTemplate默认如果保存对象,使用jdk序列化机制
10.3.2.2改变jdk的序列化规则 (RedisConfiguration)
4.关闭并重启服务器(指定配置文件)和客户端,在客户端获取,结果如下:
NoSql,叫非关系型数据库,它的全名Not only sql。为了解决高并发、高可用、高可扩展,大数据存储等一系列问题而产生的数据库解决方案,就是NoSql。它不能替代关系型数据库,只能作为关系型数据库的一个良好补充。
相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
典型应用: 内容缓存,主要用于处理大量数据的高访问负载。
数据模型: 一系列键值对
优势: 快速查询
劣势: 存储的数据缺少结构化
相关产品:Cassandra, HBase, Riak
典型应用:分布式的文件系统
数据模型:以列簇式存储,将同一列数据存在一起
优势:查找速度快,可扩展性强,更容易进行分布式扩展
劣势:功能相对局限
相关产品:CouchDB、MongoDB
典型应用:Web应用(与Key-Value类似,Value是结构化的)
数据模型: 一系列键值对
优势:数据结构要求不严格
劣势: 查询性能不高,而且缺乏统一的查询语法
相关数据库:Neo4J、InfoGrid、Infinite Graph
典型应用:社交网络
数据模型:图结构
优势:利用图结构相关算法。
劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。
Redis是使用c语言开发的一个高性能键值数据库。Redis可以通过一些键值类型来存储数据。
键值类型:
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。
缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用)
分布式集群架构中的session分离。
聊天室的在线好友列表。
任务队列。(秒杀、抢购、12306等等)
应用排行榜。
网站访问统计。
数据过期处理(可以精确到毫秒)
window版下载地址:https://github.com/MicrosoftArchive/redis/releases
下载Redis For window X64.zip,解压到指定目录
Redis.windows.conf:redis的配置文件。文件内可以声明redis的端口号,database的数量,密码,是否后台运行,持久化机制,日志等信息。
Redis-server.exe:redis服务端,可以启动redis服务。
Redis-cli.exe: redis自带客户端,启动以后即可连接服务端,通过以命令的形式向redis服务中存取数据,也可以关闭redis服务。
双击 redis-server.exe ,启动成功,出现如下界面。端口号6379,pid:16316,如果关闭当前窗口,相当于关闭redis服务。
或者以命令行的方式启动redis,在当前目录下输入redis-server即可启动redis服务
双击redis-cli.exe即可启动客户端:如下图表示,则已经连上服务端,可以通过命令存取数据了。
如果服务端设置了密码,在运行客户端连接服务端时,需要加上密码
./redis-cli -a 123456 -------123456为密码
或者
RedisDesktopManager以图形化界面的方式操作redis中的数据。安装redis桌面管理工具。
建立连接:
输入名称,主机ip,默认端口号6379,即可连接redis服务器。
Redis默认有16个数据库,如果不指定,默认使用database0
向第一个数据库中添加,修改,删除数据。
Redis启动时可以不指定配置文件,会使用默认配置启动。也可以指定配置文件。如果我们修改了配置文件的内容,希望指定以某个配置文件启动,则使用如下方法启动
复制配置文件redis.windows.conf,改名为redis.conf,修改使用密码123456登录。
以该配置文件运行redis服务:
此时,再通过图形化客户端连接服务时,必须指定密码才可以,否则会连接失败。
官网:https://redis.io/download,选择稳定版本。
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
注意:如果在编译的时候报错如下:等等,太长了,不列举了。
m ake[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
重新测试成功。
以某个配置文件启动redis服务,一定要注意配置文件的路径
上面redis的启动方式,是前端启动,一关闭客户端,redis的服务也就停掉了,所以这种启动方式非常不友好。我们可以修改配置文件中的启动方式:
[root@localhost redis-6.0.6]# vim redis.conf
进到redis.conf文件里,然后找到daemonize no把no改为yes
启动的时候指定修改之后的配置文件即可。
或者:
如果redis设置了密码,则以下面方式关闭,123456为密码
修改redis配置文件,注释掉bind 127.0.0.1
保护模式关闭:
设置密码:
1.把redis加入service服务
vim /lib/systemd/system/redis.service
2.写入
[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
保存,退出。
3.运行以下
[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运行状态
把缓存键值对清空。
语法:set key value
语法:get key
语法:GETSET key value
语法:
mset key value [key value …]
mget key [key …]
语法:del key
127.0.0.1:6379> del test
(integer) 1
递增数字
当存储的字符串是整数时,Redis提供了一个实用的命令INCR(increate),其作用是让当前键值递增,并返回递增后的值。
语法:incr key
增加指定的整数
语法:incrby key increment
递减数值
语法:decr key 可以减为负值
减少指定的整数
语法:decrby key decrement
散列类型
假设有User对象以JSON序列化的形式存储到Redis中,User对象有id,username、password、age、name等属性,存储的过程如下:
保存、更新:
User对象 --> json(string) --> redis
如果在业务上只是更新age属性,其他的属性并不做更新我应该怎么做呢? 如果仍然采用上边的方法在传输、处理时会造成资源浪费,下边讲的hash可以很好的解决这个问题。
hash叫散列类型,它提供了字段名和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:
hset命令不区分插入和更新操作,执行插入操作时HSET命令返回1,执行更新操作时返回0。
一次只能设置一个字段值
语法:hset key field value
一次可以设置多个字段值
语法:hmset key field value [field value ...]
当字段不存在时赋值,类似hset,区别在于如果字段存在,该命令不执行任何操作
语法:hsetnx key field value
如果user中没有age字段则设置age值为30,否则不做任何操作
一次只能获取一个字段值
语法:hget key field
一次可以获取多个字段值
语法:hmget key field [field ...]
获取所有字段值
语法:hgetall key
可以删除一个或多个字段,返回值是被删除的字段个数
语法:hdel key field [field ...]
语法:hincrby key field increment
Redis的list是采用来链表来存储的,所以对于redis的list数据类型的操作,是操作list的两端数据来操作的。
向列表左边增加元素
语法:lpush key value [value ...]
向列表右边增加元素
语法:rpush key value [value ...]
lrange命令是列表类型最常用的命令之一,获取列表中的某一片段,将返回start、stop之间的所有元素(包含两端的元素),索引从0开始。索引可以是负数,如:“-1”代表最后边的一个元素。
语法:lrange key start stop
127.0.0.1:6379> lrange list1 0 -1 查所有
往左边存,先存1,lpush是往左边放,2放在1的左边,3放在2的左边,依次往前
lpop命令从列表左边弹出一个元素,会分两步完成:
第一步是将列表左边的元素从列表中移除
第二步是返回被移除的元素值。
语法:
lpop key
rpop key
语法:llen key
集合类型
集合类型:无序、不可重复
列表类型:有序、可重复
语法:sadd key member [member ...]
语法:srem key member [member ...]
语法:smembers key
语法:sismember key member
属于A并且不属于B的元素构成的集合。
语法:sdiff key [key ...]
属于A且属于B的元素构成的集合。
语法:sinter key [key ...]
属于A或者属于B的元素构成的集合
语法:sunion key [key ...]
Sortedset又叫zset
Sortedset是有序集合,可排序的,但是唯一。
Sortedset和set的不同之处,是会给set中的元素添加一个分数,然后通过这个分数进行排序。
向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。
语法:zadd key score member [score member ...]
语法:zscore key member
移除有序集key中的一个或多个成员,不存在的成员将被忽略。
当key存在但不是有序集类型时,返回一个错误。
语法:zrem key member [member ...]
获得排名在某个范围的元素列表
按照元素分数从小到大的顺序返回索引从start到stop之间的所有元素(包含两端的元素)
语法:zrange key start stop [withscores]
按照元素分数从大到小的顺序返回索引从start到stop之间的所有元素(包含两端的元素)
语法:arevrange key start stop [withscores]
如果需要获得元素的分数的可以在命令尾部加上withscores参数
从小到大
语法:ZRANK key member
从大到小
语法:ZREVRANK key member
返回满足给定pattern 的所有key
确认一个key 是否存在
示例:从结果来看,数据库中不存在HongWan 这个key,但是age 这个key 是存在的
删除一个key
重命名key
示例:k2成功的被我们改名为k2了
返回值的类型
示例:这个方法可以非常简单的判断出值的类型
Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即:到期后数据销毁。
expire key seconds 设置key的生存时间(单位:秒)key在多少秒后会自动删除
ttl key 查看key剩余的生存时间
persist key 清除生存时间 -1
pexpire key milliseconds 生存时间设置单位为:毫秒
-1:清除生存时间,redis一直存在该key
-2:到期,redis没有该key
正整数:剩余的生存时间
手机号验证码
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
处理字符串相关操作
- 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.HashOperations;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
-
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
-
- @SpringBootTest
- class ApplicationTests {
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
-
- @Test
- public void testString() {
- // System.out.println(stringRedisTemplate);
- ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
- valueOperations.set("k1", "v1");
- valueOperations.set("k2", "v2");
- String v1 = valueOperations.get("k1");
- System.out.println(v1);
- //存储多对key和value
- Map<String, String> map = new HashMap<>();
- map.put("k3", "v3");
- map.put("k4", "v4");
- valueOperations.multiSet(map);
- }
- }
- 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.HashOperations;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
-
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
-
- @SpringBootTest
- class ApplicationTests {
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
-
- @Test
- public void testHash() {
- // HashOperations操作Hash类型的数据
- HashOperations<String, Object, Object> hashOperations = stringRedisTemplate.opsForHash();
- //存储单个字段名和字段值
- hashOperations.put("user", "username", "zhangsan");
- hashOperations.put("user", "age", "18");
- //存储多个字段名和字段值
- Map<String, String> map = new HashMap<>();
- map.put("sex", "男");
- map.put("email", "aaa.@163.com");
- hashOperations.putAll("user", map);
- //获取单个值
- Object username = hashOperations.get("user", "username");
- System.out.println("username" + username);
- //获取多个值
- List<Object> hashKeys = Arrays.asList("username", "age", "sex", "email");
- List<Object> user = hashOperations.multiGet("user", hashKeys);
- System.out.println("user的多个hash值:" + user);
- }
- }
- 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.HashOperations;
- import org.springframework.data.redis.core.ListOperations;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
-
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
-
- @SpringBootTest
- class ApplicationTests {
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
-
- @Test
- public void testList() {
- // ListOperations 操作List数据类型的
- ListOperations<String, String> listOperations = stringRedisTemplate.opsForList();
- // 从左边添加一个数据
- listOperations.leftPush("list", "1");
- listOperations.leftPush("list", "2");
- // 从左边添加多个数据
- listOperations.leftPushAll("list", "3", "4");
- // 从右边添加一个数据
- listOperations.rightPush("list", "5");
- listOperations.rightPush("list", "6");
- // 从右边添加多个数据
- listOperations.rightPushAll("list", "7", "8");
-
- //获取所有数据
- List<String> list = listOperations.range("list", 0, -1);
- System.out.println(list);
- //从左边弹出数据
- String leftPopValue = listOperations.leftPop("list");
- System.out.println(leftPopValue);
- //从右边弹出数据
- String rightPopValue = listOperations.rightPop("list");
- System.out.println(rightPopValue);
- //再次获取全部数据
- List<String> list1 = listOperations.range("list", 0, -1);
- System.out.println(list1);
- }
- }
List可重复添加 [二次刷新后]
- 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.*;
-
- import java.util.*;
-
- @SpringBootTest
- class ApplicationTests {
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
-
- @Test
- public void testSet() {
- // SetOperations 操作set类型数据
- SetOperations<String, String> setOperations = stringRedisTemplate.opsForSet();
- //添加,可以添加多个值
- setOperations.add("setA", "a", "b", "c", "a");
- //获取所有值
- Set<String> setA = setOperations.members("setA");
- System.out.println("setA=" + setA);
-
- setOperations.add("setB", "b", "c", "d");
- Set<String> setB = setOperations.members("setB");
- System.out.println("setB=" + setB);
-
- //差集difference
- Set<String> difference = setOperations.difference("setA", "setB");
- System.out.println("setA差集setB:" + difference);
- //交集intersect
- Set<String> intersect = setOperations.intersect("setA", "setB");
- System.out.println("setA交集setB:" + intersect);
- //并集union
- Set<String> union = setOperations.union("setA", "setB");
- System.out.println("setA并集setB:" + union);
- }
- }
- 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.*;
-
- import java.util.*;
-
- @SpringBootTest
- class ApplicationTests {
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
-
- @Test
- public void testZSet() {
- // ZSetOperations 操作 可以排序的集合类型
- ZSetOperations<String, String> zSetOperations = stringRedisTemplate.opsForZSet();
- //添加一个值
- zSetOperations.add("score", "jack", 60);
- Set<ZSetOperations.TypedTuple<String>> set = new HashSet<>();
- //添加多个值
- DefaultTypedTuple<String> typedTupleForJim = new DefaultTypedTuple<>("jim", new Double(80));
- DefaultTypedTuple<String> typedTupleForTom = new DefaultTypedTuple<>("tom", new Double(70));
- set.add(typedTupleForJim);
- set.add(typedTupleForTom);
- zSetOperations.add("score", set);
-
- //获取值
- Set<ZSetOperations.TypedTuple<String>> score = zSetOperations.rangeWithScores("score", 0, -1);
- System.out.println(score);
- //遍历
- //for (ZSetOperations.TypedTuple<String> stringTypedTuple : score) {
- // System.out.println(stringTypedTuple);
- //}
- }
- }
处理对象相关操作
实体类User
- 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 + '\'' +
- '}';
- }
- }
UserService伪代码(不在本案例中)
- package com.tjetc.service;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.stereotype.Service;
-
- import java.util.List;
-
- @Service
- public class UserService {
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
-
- public List<String> find() {
- //从redis中查询
- ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
- String value = valueOperations.get("k1");
- if (value == null) {
- //调用dao,从数据库查询
- //存储到redis一份数据
- valueOperations.set("k1", "v1");
- //组装数据,返回
- return null;
- } else {
- //组装数据,返回数据value
- return null;
- }
- }
- }
RedisTemplateTest
- 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 RedisTemplateTest {
- @Autowired
- private RedisTemplate redisTemplate;
-
- @Test
- public void test() {
- //创建一个user对象
- User user = new User();
- user.setId(1L);
- user.setUsername("wangxiao");
- user.setPassword("8595105");
-
- ValueOperations valueOperations = redisTemplate.opsForValue();
- //对象序列化二进制数据后存储到redis中
- valueOperations.set("u", user);
-
- //获取user
- User u = (User) valueOperations.get("u");
- System.out.println("user=" + u);
- }
- }
①在配置类中配置自定义序列化规则
- 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.StringRedisSerializer;
-
- @Configuration
- public class RedisConfiguration {
- @Bean
- public RedisTemplate userRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
- //创建RedisTemplate对象
- RedisTemplate<String, Object> template = new RedisTemplate<>();
- //设置连接工厂
- template.setConnectionFactory(redisConnectionFactory);
- //创建Jackson2JacksonRedisSerializer对象,使用Jackson把对象转换json字符串,设置序列化方式
- GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(String.valueOf(Object.class));
- //template对象设置value的序列化方式,设置默认序列化
- template.setDefaultSerializer(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 RedisTemplateTest {
- @Autowired
- private RedisTemplate<String,User> redisTemplate;
-
- @Test
- public void test1() {
- //创建一个user对象
- User user = new User();
- user.setId(1L);
- user.setUsername("wangxiao");
- user.setPassword("8595105");
-
- //对象序列化二进制数据后存储到redis中
- ValueOperations<String,User> valueOperations = redisTemplate.opsForValue();
- valueOperations.set("u1", user);
-
- //获取user
- User u1 = valueOperations.get("u1");
- System.out.println("user=" + u1);
- }
- }
持久化的功能:Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。
Redis持久化分为RDB持久化和AOF持久化:前者将当前数据保存到硬盘,后者则是将每次执行的写命令保存到硬盘(类似于MySQL的binlog);由于AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此AOF是目前主流的持久化方式,不过RDB持久化仍然有其用武之地。
下面依次介绍RDB持久化和AOF持久化;由于Redis各个版本之间存在差异,如无特殊说明,以Redis3.0为准。
默认方式,不需要进行配置,默认就使用这种机制。
在一定的间隔时间中,检测key的变化情况,然后持久化数据
# after 900 sec (15 min) if at least 1 key changed
save 900 1
# after 300 sec (5 min) if at least 10 keys changed
save 300 10
# after 60 sec if at least 10000 keys changed
save 60 10000
如果我们将配置该成如下形式:
save 900 1
save 300 10
save 20 5 在20秒之后,如果有5个键发生了改变,则执行持久化
上述操作在20秒内完成。这时在当前目录下会出现一个rdb文件。
关闭服务器和客户端。
在客户端直接获取username,结果如下:
日志记录的方式,可以记录每一条命令的操作。可以每一次命令操作后,持久化数据。
aof持久化文件名称
appendonly no(关闭aof) --> appendonly yes (开启aof)
AOF相关配置:
# appendfsync always 每一次操作都进行持久化
appendfsync everysec 每一秒进行一次持久化,默认方式
# appendfsync no 不进行持久化
127.0.0.1:6379> set username lisi
OK
127.0.0.1:6379> set password 123
OK
127.0.0.1:6379> keys *
1) "password"
2) "username"
127.0.0.1:6379> get username
"lisi"
127.0.0.1:6379> get password
"123"
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路 I/O 复用模型,非阻塞 IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。