Redis
Redis简介
Remote Dictionary Server(Redis)是一个开源的使用 ANSIC 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。
它通常被称为数据结构服务器,因为值(value)可以是字符串(String), 哈希(Map),列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
Redis的特点
- 支持多种数据结构,如 string(字符串)、 list(双向链表)、dict(hash 表)、set(集合)、zset(排序 set)、hyperloglog(基数估算)等等
- 支持持久化操作,可以进行
aof及rdb数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。 - 支持通过 Replication 进行数据复制,通过 master-slave 机制,可以实时进行数据的同步复制,支持多级复制和增量复制,master-slave 机制是 Redis 进行 HA 的重要手段。
- 单进程请求,所有命令串行执行,并发情况下不需要考虑数据一致性问题(解决超卖问题)。
Redis单机版的安装
第一步
需要在linux系统中安装gcc
yum install -y gcc-c++
第二步
需要将下载好的 redis 压缩包添加到 linux 服务器(官网下载 redis.io/download)
第三步
解压压缩包
tar -zxvf redis......(这是你要解压的redis名字)
第四步
编译redis
进入 redis 的解压完毕的根目录下 执行命令:make
第五步
安装 redis
进 入 redis 的 解 压 完 毕 的 根 目 录 下 ,执 行 命 令 :
make install PREFIX=/usr/local/redis
第六步
启动 redis
-
前端启动
在 bin 目录下执行命令: ./redis-server (ctrl+c)退出 redis
出现上面的表示redis启动成功,但是启动之后我们在当前环境下没法进行其他的操作,所以要改成后端启动
-
后端启动
-
先将 redis 解压目录下的 redis.conf 文件拷贝到 安装好的 redis 的 bin 目录下命令
cp redis.conf /usr/local/redis/bin -
修改拷贝过来的 redis.conf 配置文件
vim redis.conf 将daemonize no 改为 yes -
启动 redis:在后台启动redis bin 目录下执行
./redis-server redis.conf -
查看 redis 启动是否成功
ps aux|grep redis -
进入redis
./redis-cli -
关闭 redis 的命令
./redis-cli shutdown -
第七步:测试 redi
在bin 目录下启动 redis 自带的客户端 ./redis-cli 后可以开始操作redis的数据了 常见 redis 命令: ping返回>pong 说明客户端连接上了redis
-
Redis数据类型
String(字符串)
Redis 字符串是字节序列。Redis 字符串是二进制安全的,这意味着他们有一个已知的
127.0.0.1:6379> set name xiao
OK
127.0.0.1:6379> get name
"xiao"
127.0.0.1:6379>
incr让当前键值以 1 的数量递增,并返回递增后的值incrby可以指定参数一次增加的数值,并返回递增后的值decr让当前键值以 1 的数量递减 并返回递减后的值decrby可以指定参数一次递减的数值,并返回递减后的值incrbyfloat可以递增一个双精度浮点数append作用是向键值的末尾追加 value。如果键不存在则将该键的值设置为 value。返回值是追加后字符串的总长度。- mget/mset 作用与 get/set 相似,不过 mget/mset 可以同时获得/设置多个键的键值
del根据 key 来删除 valuekeys *:显示当前redis中所有的key
Hash哈希
Redis 的哈希是键值对的集合。 Redis 的哈希值是字符串字段和字符串值之间的映射,因此它们被用来表示对象。
- hset 存储一个哈希键值对的集合
hset key field value- hget 获取一个哈希键的值
hget key field- hmset 存储一个或多个哈希是键值对的集合
hmset key field1 value1 ......fieldN keyN- hmget 获取多个指定的键的值
hmget key field1 ... fieldN- hexists 判断哈希表中的字段名是否存在 如果存在返回 1 否则返回 0
hexists key field- hdel 删除一个或多个字段
hdel key field- hgetall 获取一个哈希是键值对的集合
hgetall key- hvals 只返回字段值
hvals key
List链表
Redis 的链表是简单的字符串列表,排序插入顺序。可以添加元素到 Redis 的列表的头部或尾部(可以理解为java中linkedlist)队列的特点:FIFO(先进的先出)
lpush key value向链表左侧添加rpush key value向链表右侧添加lrange key index1 index2遍历链表:取index1到index2之间的元lrange c 0 -1遍历整个c键中的元素-1代表索引最后一个lpop从链表的左边取数据,并且移除rpop从链表的右边取数据,并且移除lindex key indexnumber如果要将链表类型当做数组来用,lindex 命令是必不可少的。lindex命令用来返回指定索引的元素,索引从 0 开始,如果是负数表示从右边开始计算的索引,最右边元素的索引是-1Lset key indexnumber value是另一个通过索引操作列表的命令,它会将索引为 index 的元素的值修改为 value。
Redis的数据持久化
RDB方式(默认的备份方式)
RDB 方式:将 Redis 在内存中的数据库状态保存到磁盘里面,RDB 文件是一个经过压缩的二进制文件,通过该文件可以还原生成 RDB 文件时的数据库状态(默认下,持久化到 dump.rdb 文件,并且在 redis 重启后,自动读取RDB文件中的数据,据悉,通常情况下一千万条字符串类型键,或1GB大小 的快照文件,同步到内存中的时间是 20-30 秒)
生成方式
-
执行命令手动生成
有两个 Redis 命令可以用于生成 RDB 文件,一个是
SAVE,另一个是BGSAVE;SAVE命令会阻塞 Redis 服务器进程,直到 RDB 文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求BGSAVE命令会派生出一个子进程,然后由子进程负责创建 RDB 文件,服务器进程(父进程)继续处理命令请求,创建 RDB 文件结束之前,客户端发送的 BGSAVE 和 SAVE 命令会被服务器拒绝
-
通过配置自动生成(默认开启)
可以设置服务器配置的 save 选项,让服务器每隔一段时间自动执行一次 BGSAVE 命令,可以通过 save 选项设置多个保存条件,但只要其中任意一个条件被满足,服务器就会执行
BGSAVE命令例如:
save 900 1 save 300 10 save 60 10000那么只要满足以下三个条件中-的任意一个,save 命令就会被执行服务器在
900 秒之内,对数据库进行了至少 1 次修改服务器在
300 秒之内,对数据库进行了至少 10 次修改服务器在
60 秒之内,对数据库进行了至少 10000 次修改
AOF方式
AOF 持久化方式在 redis 中默认是关闭的,需要修改配置文件开启该方式。
AOF:把每条命令都写入文件,类似 mysql 的 binlog 日志
AOF 方式:是通过保存 Redis 服务器所执行的写命令来记录数据库状态的文件。
AOF 文件刷新的方式,有三种存储策略:
appendfsync always
每提交一个修改命令都调用 fsync 刷新到 AOF 文件,非常非常慢,但也非常安全
appendfsync everysec
每秒钟都调用 fsync 刷新到 AOF 文件,很快,但可能会丢失一秒以内的数据
appendfsync no
依靠 OS(操作系统) 进行刷新,redis 不主动刷新 AOF,这样最快,但安全性就差默认并推荐每秒刷新,这样在速度和安全上都做到了兼顾
我使用的是第三种
手动开启aop持久化方案,数据不仅会持久化到aop中,还有持久化到rdb中,这样的持久化数据有两份,但是aop开启的时候,数据恢复是从aop文件中恢复的,只有在关闭(appendonly no)时,才会从rdb中恢复数据
AOF 数据恢复方式
服务器在启动时,通过载入和执行 AOF 文件中保存的命令来还原服务器关闭之前的数据库状态,具体过程:
-
载入AOF文件模拟客户端从AOF 文件中读取命令使用模拟客户端执行命令循环读取并执行命令,直到全部完成。
-
如果同时启用了 RDB 和 AOF 方式,AOF 优先,启动时只加载 AOF 文件恢复数据,想要加载RDB需要把AOF关闭。
Redis在SpringBoot中的应用
-
首先将redis需要的包导入
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.6.3</version> </dependency> -
将redis连接的配置文件
application.yml配置好spring: redis: host: 填入你的IP port: 6379 -
需要对
redis.conf中进行修改,不然会连接失败进入文件将bind 127.0.0.1 -::1注释 protected-mode yes 改为 protected-mode no -
准备工作做完之后,便可以测试
redisTemplate的方法了package com.my; import com.my.redis.RedisApp; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.test.context.junit4.SpringRunner; import java.io.Serializable; import java.util.Set; import java.util.concurrent.TimeUnit; //表示基于springboot的测试依赖当前工程的启动类 @SpringBootTest(classes = RedisApp.class) @RunWith(SpringRunner.class) public class MyTest { //注入可以操作redis的模板对象 @Autowired private RedisTemplate redisTemplate; /** * 操作String类型 */ @Test public void testRedis_String(){ Student student = new Student("小明", 18); //设置键为name,值为lili redisTemplate.boundValueOps("name").set("lili"); redisTemplate.boundValueOps("age").set(19); redisTemplate.boundValueOps("student").set(student); //取数据,默认提升为Object需要转换 String name = (String)redisTemplate.boundValueOps("name").get(); Integer age = (Integer)redisTemplate.boundValueOps("age").get(); //取出存入的对象 Student stu = (Student)redisTemplate.boundValueOps("student").get(); System.out.println(stu); System.out.println(name); System.out.println(age); /** * 设置过期时间 * 第一个参数时数目 * 第二个参数时单位 */ redisTemplate.boundValueOps("name").expire(1, TimeUnit.MINUTES); //删除key Boolean delete = redisTemplate.delete("age"); } /** * 操作Hash类型 */ @Test public void testRedis_Hash(){ redisTemplate.boundHashOps("user").put("name","小明"); //获取到值 String name = (String)redisTemplate.boundHashOps("user").get("name"); } /** * 操作List类型 */ @Test public void testRedis_List(){ redisTemplate.boundListOps("cities").leftPush("北京"); redisTemplate.boundListOps("cities").rightPush("上海"); //获取到值 String city1 = (String)redisTemplate.boundListOps("cities").leftPop(); String city2 = (String)redisTemplate.boundListOps("cities").rightPop(); } /** * 操作Set类型 */ @Test public void testRedis_Set(){ redisTemplate.boundSetOps("province").add("湖南"); redisTemplate.boundSetOps("province").add("湖北"); redisTemplate.boundSetOps("province").add("广西","广东"); //打印集合 Set province = redisTemplate.boundSetOps("province").members(); } } //存入redis的对象一定要序列化 class Student implements Serializable { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + ''' + ", age=" + age + '}'; } } 当你在虚拟机redis中查看时会发现key带有前缀,是因为
RedisTemplate默认使用反序列化工具,但不影响我们使用,这也是为什么在构建POJO类时需要让他可序列化