引言
Redis是一种基于键值对(key-value)的高性能的NoSql数据库,它提供了各种数据结构存储,具有高性能的读写功能,是目前企业中应用极其广泛的一种数据库。
官网:https://redis.io
1. NoSql数据库简介
1.1 技术发展
web1.0时代,数据访问量很有限,用高性能的单点服务器可以解决大部分问题。随着web2.0时代的到来后,用户访问量大幅度的提升,同时产生了大量的用户数据,加上后来的智能移动设备的普及,所有的互联网平台都面临了巨大的性能挑战,其实主要的挑战来自于服务器的CPU及内存压力以及MySql的读写IO压力 。
1.2 NoSql数据库
1.2.1 NoSql的概念
NoSql不要理解成“不是SQL”,是指“Not Only Sql”,意为不仅仅是Sql,它里面既有SQL,还有SQL不能解决的问题,泛指非关系型数据库。NoSql不依赖业务逻辑的方式存储,而以简单的Key-Value模式存储,因此大大的增加了数据库的扩展能力。
-
不遵守SQL标准
-
不支持ACID
-
远超于SQL的性能
1.2.2 适用场景
-
对数据高并发的读写
-
海量数据的读写 Nosql吞吐量
1.2.3 常见的NoSql数据库
-
Memcache
-
是一款很早就出现的数据库
-
数据都在内存中,一般 不支持持久化
-
支持简单的key-value模式,支持类型单一。
-
一般是作为缓存数据库辅助持久化的数据库。
-
-
Redis
-
几乎覆盖了Memcache的绝大部分功能
-
数据都在内存中,支持持久化,主要用作备份恢复。
-
除了支持简单的key-value模式,还支持多种数据结构的存储,比如list、set、hash、zset等
-
也是作为缓存数据库辅助持久化的数据库。
-
-
MongoDB
-
高性能,开源、模式自由的文档型数据库
-
数据都在内存中,如果内存不足,把不常用的数据保存在硬盘中
-
虽然是key-value模式,但是对value,尤其是json提供了丰富的查询功能
-
支持二进制及大型对象
-
2. Redis概述和安装
2.1 Redis概述
Redis 是完全开源免费的,是一个高性能的key-value数据库。Redis的性能十分优越,可以支持每秒十几万的读/写操作,其性能远超关系型数据库,并且支持集群、分布式、主从同步等配置,还支持一定事务的能力。
2.2 Redis在JavaWeb中的应用
在互联网的应用中,往往存在一些需要高速读/写的场合,比如商品的秒杀,抢红包,淘宝、京东的双十一活动或者春运抢票等。这类场合在一个瞬间成千上万的请求就会达到服务器,如果使用的是关系型数据库, 一个瞬间数据库就需要执行成千上万的SQL ,很容易造成数据库的瓶颈,严重的会导致数据库瘫痪,造成Java Web 系统服务崩溃。
在这样的场合的应对办法往往是考虑异步写入数据库,而在高速读/写的场合中单单使用Redis 去应对, 把这些需要高速读/写的数据, 缓存到Redis 中,而在满足一定的条件下,触发这些缓存的数据写入数据库中。
咱们再进一步深入论述这个过程: 当一个请求达到服务器,只是把业务数据先在Redis 读/写,而没有进行任何对数据库的操作,换句话说系统仅仅是操作Redis 缓存,而没有操作数据库,这个速度就比操作数据库要快得多,从而达到需要高速响应的效果。
但是一般缓存不能持久化,因此需要把这些数据存入数据库,所以在一个请求操作完Redis 的读/写后,会去判断该高速读/写的业务是否结束。这个判断的条件往往就是秒杀商品剩余个数为0 ,抢红包金额为0 ,如果不成立,则不会操作数据库;如果成立,则触发事件将Redis 缓存的数据以批量的形式一次性写入数据库,从而完成持久化的工作。
假设面对的是一个商品秒杀的场景,从上面的流程看, 一个用户抢购商品,绝大部分的场合都是在操作内存数据库Redis , 而不是磁盘mysql数据库,所以其性能更为优越。只有在商品被抢购一空后才会触发系统把Redis 缓存的数据写入数据库磁盘中, 这样系统大部分的操作基于内存,就能够在秒杀的场合高速响应用户的请求,达到快速应答。
2.3 Redis的安装
2.3.1 Windows安装步骤
1.下载安装包
**下载地址:**https://github.com/dmajkic/redis
2.打开你刚才解压的文件路径
3.双击redis-server.exe启动服务
4.使用redis-cli.exe客户端来连接
5.测试连接
输入ping命令,输出PONG代表连接成功
2.3.2 Linux安装步骤
卸载步骤:
查看是否已经安装redis,版本
redis-server -v | redis-cli -v
如果存在
ps - ef | grep redis
关闭服务或者kill进程
kill -9 01234
删除安装目录下的文件
rm -rf /usr/local/bin/redis*
1.在线安装gcc环境
yum install gcc-c++
安装完毕后,查看版本号
gcc -v
2.下载安装包
官网:https://redis.io/
打开中文官网下载:http://www.redis.cn/
3.上传到linux服务器/opt文件下
4.解压压缩包
tar -zxvf redis-6.2.5.tar.gz
5.进入redis-6.2.5
cd redis-6.2.5
6.执行make命令
make
如果出现:致命错误:jemalloc/jemalloc.h:没有那个文件或目录,则执行下面命令,进行残存文件的清理
make distclean
后,继续执行 make命令,执行完毕后,可再make一次,然后就执行很快了
7.执行命令
make install
redis的默认安装路径和java是一样的,也在/usr下面
8.进入usr目录,找到local下面的bin目录,就可以看到redis-server,自此redis安装就完成了
cd /usr/local/bin
2.3.3 Linux下配置Redis
1.在安装的bin目录下创建文件夹
mkdir redisconfig
2.拷贝文件
cp /opt/redis-6.2.5/redis.conf redisconfig
以后使用这个redisconfig下面的redis.conf就好了,原来的解压目录下的配置文件不需要管了
3.进行后台启动
因为redis不是默认后台启动的,所以得修改配置文件
vim redis.conf
找到daemonize no,把no修改为yes,保存并退出,这样redis就会以后台方式启动
4.启动redis服务
回到 /usr/local/bin目录,运行
cd /usr/local/bin
redis-server redisconfig/redis.conf
意思是通过指定的配置文件redisconfig/redis.conf来启动
5.使用客户端进行连接
redis-cli -p 6379
输入命令
ping
显示PONG,就表示连接成功!
6.测试连接
keys *
set name mengshujun
get name
可以查看里面还有哪些键
7.查看redis的进程是否开启
在xshell中,复制一个会话,输入
ps -ef | grep redis
8.关闭redis服务
在127.0.0.1:6379> 下输入
shutdown
exit
3. Redis配置文件
3.1 远程连接配置
bind 127.0.0.1 -:: 1 表示只能接受本机的访问,要通过其他电脑远程进行连接就应该注释,找到后注释掉,默认没有注释。
protected-mode yes 表示开启保护模式,只能通过本机访问,远程不能访问,改为no就可以进行远程访问了
3.2 配置连接密码
修改redis.conf配置文件
# requirepass foobared
requirepass 866qa@WSDE#
保存后重启redis就可以了
小技巧:
我们通常在vim下要查找字符串的时候, 都是输入 / 或者 ? 加 需要查找的字符串来进行搜索,比如想搜索 requirepass这个单词, 可以输入 /requirepass或者 ?requirepass, 两者的区别是前者是从上往下搜索,后者是从下往上搜索。按n往下搜索。
3.3 关闭linux防火墙
查看linux的防火墙是否关闭
#查看防火墙的启动状态
systemctl status firewalld
# 关闭防火墙
systemctl stop firewalld
#开启防火墙
systemctl start firewalld
#查看开放的端口号
firewall-cmd --list-all
#在防火墙中设置开放的端口号
firewall-cmd --zone=public --add-port=6379/tcp --permanent
#开启端口后,需要重启防火墙才生效
systemctl restart firewalld | firewall-cmd --reload
# 如果有不用的端口,则进行移除
firewall-cmd --permanent --remove-port=8001/tcp
3.4 连接redis服务
1.xshell连接
redis-cli -h host -p port -a password
#host:远程redis服务器host
#port:远程redis服务端口
#password:远程redis服务密码(无密码的的话就不需要-a参数了)
示例:
redis-cli -h 127.0.0.1 -p 6379 -a 123456
或者,如果有密码,连接的时候也可输入,回车即可。
auth 123456
2.redisdesktop工具连接
4. 常用五大数据类型
基础命令学习
针对key的操作:
redis默认有16个数据库,可以打开相关配置文件进行查看,默认使用的是第0个数据库。
select 【num】 切换数据库,num代表0-15的数字
dbsize 可以查看数据库的大小,里面包含的key的个数
flushdb 清空当前库
flushall 清空(通杀)所有的库
exists key 判断某个Key是否存在
del key删除指定的key数据
unlink key 根据value选择非阻塞删除,仅将key从keyspace元数据中删除,真正的删除会在后续慢慢异步操作
type key 查看key对应的value是什么类型
expire key 10 为给定的key设置过期时间10秒钟
ttl key 查看还有多少秒过期 -1表示永不过期 -2表示已经过期
4.1 Redis字符串(String)
String是Redis最基本的类型, 一个key对应一个value,一个value的最多是512MB。String类型是二进制的安全的,意味着Redis的String可以包含任何数据。比如jpg图片或者序列化对象。
序号 | 命令及描述 |
---|---|
1 | SET key value 设置指定 key 的值 |
2 | GET key 获取指定 key 的值。 |
3 | GETRANGE key start end 返回 key 中字符串值的子字符 |
4 | GETSET key value 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 |
5 | SETNX key value 只有在 key 不存在时设置 key 的值。 分布式锁 |
6 | STRLEN key 返回 key 所储存的字符串值的长度。 |
7 | MSET key value [key value ...] 同时设置一个或多个 key-value 对。 |
8 | MGET key1 [key2..] 获取所有(一个或多个)给定 key 的值。 |
9 | MSETNX key value [key value ...] 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。 |
10 | INCR key 将 key 中储存的数字值增一。 |
11 | DECR key将 key 中储存的数字值减一。 |
12 | INCRBY key increment 将 key 所储存的值加上给定的增量值(increment) 。 |
13 | DECRBY key decrementkey 所储存的值减去给定的减量值(decrement) 。 |
14 | APPEND key value 如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾。 |
4.2 Redis列表(List)
特征:有序的,不唯一的
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),它的底层实际是一个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能可能会较差。
一个列表最多可以包含4294967295个元素, 超过40亿个元素。
序号 | 命令及描述 |
---|---|
1 | LPUSH key value1 value2 在列表左边添加将一个或多个值 |
2 | RPUSH key value1 value2 在列表右边添加添加一个或多个值 |
3 | LRANGE key 0 -1 获取列表指定范围内的元素 【-1代表右边第一个 0 -1代表获取所有】 |
4 | LPOP key 移出并获取列表的左边的第一个元素 【值在键在,值亡键亡】 |
5 | RPOP key 移出列表的最后一个元素,返回值为移出的元素。【值在键在,值亡键亡】 |
6 | RPOPLPUSH key1 key2 从key1列表右边移出一个值,插到key2列表左边 |
7 | LINDEX key index 通过索引获取列表中的元素 |
8 | LLEN key 获取列表长度 |
9 | LINSERT key BEFORE|AFTER value new_value 在列表的元素前或者后插入元素 |
10 | LREM key count value 移除列表元素,从左边开始删除count个元素 |
11 | LSET key index value 通过索引设置列表元素的值,会覆盖原有的值 |
4.3 Redis集合(Set)
特征:无序的,唯一的
Redis set对外提供的功能与list类似,是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择。
Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。它的内部使用hash结构,所有的value都指向同一个内部值,类似于Java中HashSet。
序号 | 命令及描述 |
---|---|
1 | SADD key member1 member2向集合添加一个或多个成员 |
2 | SMEMBERS key 返回集合中的所有成员 |
3 | SISMEMBER key member 判断 member 元素是否是集合 key 的成员 |
4 | SCARD key 获取集合的成员数 |
5 | SREM key member1 移除集合中一个或多个成员 |
6 | SPOP key 移除并返回集合中的一个随机元素 |
7 | SRANDMEMBER key count 返回集合中一个或多个随机数 |
8 | SINTER key1 key2 返回第一个集合与第二个集合之间的交集 |
9 | SUNION key1 key2 返回第一个集合与第二个集合之间的并集 |
10 | SDIFF key1 key2 返回第一个集合与第二个集合之间的差异 【key1中,不包含key2中】 |
4.4 Redis哈希(Hash)
Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。
key: 字符串名称
value:放一个对象
序号 | 命令及描述 |
---|---|
1 | HSET key field value 将哈希表 key 中的字段 field 的值设为 value 例如:hset users name zhangsan |
2 | HGET key field 获取存储在哈希表中指定字段的值。例如: hget users name |
3 | HMSET key field1 value1 field2 value2 同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
4 | HEXISTS key field 查看哈希表 key 中,指定的字段是否存在。 |
5 | HKEYS key 获取所有哈希表中的字段(键)。 |
6 | HVALS key 获取哈希表中所有值。 |
7 | HINCRBY key field increment 为哈希表 key 中的指定字段的整数值加上增量 increment 。 |
8 | HSETNX key field value 只有在字段 field 不存在时,设置哈希表字段的值。 |
4.5 Redis有序集合(Zset)
Redis 有序集合和集合一样也是 string 类型0元素的集合,且不允许重复的成员。
特征:有序的,唯一的
不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
序号 | 命令及描述 |
---|---|
1 | ZADD key score1 member1 score2 member2向有序集合添加一个或多个成员,或者更新已存在成员的分数 例如:zadd topn 100 java 200 mysql 300 linux 400 c 500 redis |
2 | ZRANGE key start stop 通过索引区间返回有序集合指定区间内的成员 例如:zrange topn 0 -1 withscores |
3 | ZRANGEBYSCORE key min max [WITHSCORES] LIMIT通过分数返回有序集合指定区间内的成员 |
4 | ZREVRANGEBYSCORE key max min 返回有序集中指定分数区间内的成员,分数从高到低排序 |
5 | ZINCRBY key increment member 有序集合中对指定成员的分数加上增量 increment |
6 | ZREM key member 移除有序集合中的一个或多个成员 |
7 | ZCOUNT key min max 计算在有序集合中指定区间分数的成员数 |
8 | ZRANK key member 返回有序集合中指定成员的索引 |
5. SpringBoot整合Redis
Spring Boot整合Redis提供了 RedisTemplate与StringRedisTemplate两个类,其中StringRedisTemplate是RedisTemplate的子类,两个方法基本一致,不同之处主要体现在操作的数据类型不同,RedisTemplate中两个泛型都是Object,意味着存储的key和value都可以是一个对象,而StringRedisTemplate的两个泛型都是String,意味着StringRedisTemplate的key和value都只能是字符串。
StringRedisTemplate<String,String>
RedisTemplate<Object,Object>
5.1 引入redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
5.2 配置设置
spring:
redis:
host: 192.168.2.167
port: 6379
password: 123456
database: 0
5.3 StringRedisTemplate应用
StringRedisTemplate主要应用于键和值都是String的情况
创建测试类测试:
package com.example.springredis;
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;
@SpringBootTest
class SpringRedisApplicationTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
public void test1(){
Set<String> keys = stringRedisTemplate.keys("*");
keys.forEach(System.out::println);
}
}
5.3.1 操作Keys
直接使用stringRedisTemplate,点方法就可以
stringRedisTemplate.type("name"); //获取类型
stringRedisTemplate.delete("name"); //删除指定key
stringRedisTemplate.hasKey("name");//判断key是否存在
stringRedisTemplate.getExpire("name");//查看过期时间 -1 代表永不超时 -2 代表过期
5.3.2 操作String
使用stringRedisTemplate.opsForValue()操作的就是字符串类型
stringRedisTemplate.opsForValue().set("gender","女");
String gender = stringRedisTemplate.opsForValue().get("gender");
System.out.println(gender);
//设置一个key超时时间
stringRedisTemplate.opsForValue().set("code","000098",120, TimeUnit.SECONDS);
5.3.3 操作List
使用stringRedisTemplate.opsForList()操作的就是list类型
stringRedisTemplate.opsForList().leftPush("names","张三");
stringRedisTemplate.opsForList().leftPushAll("names","张三","李四","王五");
List<String> list=new ArrayList<>();
list.add(0,"老大");
list.add(1,"老二");
list.add(2,"老三");
stringRedisTemplate.opsForList().leftPushAll("names",list);
List<String> names = stringRedisTemplate.opsForList().range("names", 0, -1);
names.forEach(System.out::println);
5.3.4 操作Set
使用stringRedisTemplate.opsForSet()操作的就是set类型
stringRedisTemplate.opsForSet().add("userNames","张三","张三","李四","王五");
Set<String> userNames = stringRedisTemplate.opsForSet().members("userNames");
Long size = stringRedisTemplate.opsForSet().size("userNames");
System.out.println(size);
userNames.forEach(System.out::println);
5.3.5操作zSet
使用stringRedisTemplate.opsForZSet()操作的就是zSet类型
stringRedisTemplate.opsForZSet().add("zsets", "java", 500);
stringRedisTemplate.opsForZSet().add("zsets", "spring", 200);
stringRedisTemplate.opsForZSet().add("zsets", "redis", 300);
Set<String> zsets = stringRedisTemplate.opsForZSet().range("zsets", 0, -1);
zsets.forEach(System.out::println);
System.out.println("========================");
Set<ZSetOperations.TypedTuple<String>> zsets1 = stringRedisTemplate.opsForZSet().rangeByScoreWithScores("zsets",200,500);
zsets1.forEach(item -> {
System.out.println("item"+item);
System.out.println(item.getValue());
System.out.println(item.getScore());
});
5.3.6操作Hash
使用stringRedisTemplate.opsForHash()操作的就是hash类型
stringRedisTemplate.opsForHash().put("maps","name","张三");
Object o = stringRedisTemplate.opsForHash().get("maps", "name");
System.out.println(o);
Set<Object> name = stringRedisTemplate.opsForHash().keys("maps");
name.forEach(System.out::println);
List<Object> maps = stringRedisTemplate.opsForHash().values("maps");
maps.forEach(System.out::println);
5.4 RedisTemplate应用
RedisTemplate主要应用于存储的值是对象的情况,但如果使用对象,所有的对象都必须进行序列化才可以存储。
RedisTemplate存储的时候会对键和值进行序列化操作。
5.4.1 操作普通对象
User u1 = new User("zs", 20, "15386880458");
redisTemplate.opsForValue().set("u1", u1);
User user =(User) redisTemplate.opsForValue().get("u1");
System.out.println(user);
5.4.2 操作List
@Test
public void test2() {
User u1 = new User("zs", 20, "15386880458");
User u2 = new User("lisi", 20, "15386880458");
redisTemplate.opsForList().leftPush("users",u1);
redisTemplate.opsForList().leftPush("users",u2);
List userlist = redisTemplate.opsForList().range("users", 0, -1);
userlist.forEach(System.out::println);
}
5.4.3 操作Hash
@Test
public void test4() {
User u1 = new User(100, "lxd", 20);
User u2 = new User(101, "cdp", 21);
User u3 = new User(102, "luhui", 21);
List<User> list = new ArrayList<>(); //从数据库中获取到集合数据
list.add(u1);
list.add(u2);
list.add(u3);
redisTemplate.opsForHash().put("data", "userList", list);
List<User> userList = (List<User>) redisTemplate.opsForHash().get("data", "userList");
for (User user : userList) {
System.out.println(user);
}
}
6. 自定义Template
springboot项目整合Redis记录了RedisTemplate和StringRedisTemplate的使用效果,由于分别使用了不同的序列化器,所以在Redis中存储的形式也不相同。redisTemplate使用的是默认的序列化器jdk序列化方式,而StringRedisTemplate使用了String序列化方式,如果使用默认的这种序列化方式,存入redis中的key就变成带有转义字符的,这种情况就可以自定义RedisTemplate类,一般为了开发的方便,直接定义RedisTemplate<String, Object>类型,配置内容如下,企业实际开发中,直接可以使用:
使用的依赖,springboot已经集成,无需再导入
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
配置类:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
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
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
//自定义Jackson序列化配置
Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
jsonRedisSerializer.setObjectMapper(objectMapper);
//key使用String的序列化方式
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
//hash的key也是用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
//value的key使用jackson的序列化方式
template.setValueSerializer(jsonRedisSerializer);
//hash的value也是用jackson的序列化方式
template.setHashValueSerializer(jsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}