Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。
基于内存存储的、以key = value结构存储数据、开源的、可以用于实现数据库、缓存、消息中间件的系统(从数据库角度,类似mysql,但是没有表结构,是键值对)
数据结构,以哪些结构存储数据?基本结构:字符串结构、列表结构、集合结构、有序集合结构
有事务管理、可以实现数据复制、可以实现数据的持久化、可以实现 LUA 的脚本操作
Redis 有高可用、高性能特点:采用集群保证高可用和高性能,在实现集群的时候设置了哨兵的功能。
Tomcat 的并发(同时访问一条连接) 200条 超过300宕机,需要使用集群。(搭建多个服务器,每个都相同 用户随机访问)
分布式:不同功能分不同的模块,加上集群,一堆相同的登录、相同的评论等等等。一堆相同的是一个小集群,小集群与小集群是分布式。
哨兵:用来监测集群的某个服务有没有宕机,是否正常工作,存在主服务。
端口:6379
解压: tar -xzvf
安装:make PREFIX=/usr/app/software/redis install
CentOS默认没有安装gcc,这会导致我们无法make成功。使用yum安装:
yum -y install gcc
启动
前端启动方式
通过 ./redis-server
启动 由于不能实现交互 常采用后台启动方式
后端启动方式
A、到解压文件夹中复制 redis.conf 文件到 安装目录的 bin 文件夹中
B、vi redis.conf 找 (/来进行搜索/daemonize no 改为 yes)
C、redis 先停止服务,在启动,
通过 ./redis-server redis.conf
自带的客户端
安装目录中 bin 文件夹里面的 redis-cli
./redis-cli 启动
其他客户端工具
在 Java 程序中操作 -- 如果是远程连接
A、后台启动方式
B、把端口防火墙释放
C、运行远程连接
D、可选的密码设置
全局性质的命令(是指不分数据结构,针对所有的数据结构都可以实现操作)
A、停止服务器:shutdown [NOSAVE|SAVE] 持久化与非持久化
B、退出客户端登录:exit/quit(当直接退出客户端登录,会自动持久化)(关机 内存->硬盘;开机 硬盘->内存)
C、Redis 中默认有16个库,这些库从0-15 编号,客户端登录时,默认操作的是0号库;切换仓库的操作 select n
(0号库没有数字提示)
D、可以设置数据的有效时间 expire key time
存储数据 set key value
E、查看数据的剩余有效时间,ttl key
看到 -1 表示永久有效 -2 表示原来有 但是删除了
有效期到来自动删除 正值表示剩余有效时间
F、删除数据 del key
G、查看当前库中有哪些key keys *
H、把当前库的所有数据都删除 flushdb
I、把所有库的所有数据都删除 flushall
J、查看数据所属的结构/类型 type key
K、判断当前库中是否存在某个键值对数据 exists key
1代表在 0代表无
局部性质的命令(是指不同的数据结构,操作命令不同)
A、字符串结构:string
Value 部分整体就是一个字符串
专属命令:
存字符串结构的数据:set(一次只能存一对,当前库无同名 key、如果已经有同名 key,则替换操作)、mset(一次可以存多对)、取字符串结构的数据:get(一次只能获取一个值)、mget(一次可以获取多个值)
向 value 部分追加新串:append key value
计算 value 部分字符串的长度: strlen key
截取字串: substr key startnum endnum
当 value 部分是纯数字时,可以实现值的递增或者递减操作
增/减1操作: incr key
decr key
指定增/减量:incrby key num
decrby key num
B、列表结构 --- list
Value 部分是 list 列表(有序、可重复、下标访问)
专属的命令:
存列表结构的数据:lpush、rpush
lpush key value1...
rpush key value1...
取列表结构的数据:lrange: lrange key startnum endnum
endnum 取-1 也是所有
下标访问:lindex lindex key index
删除两端数据:lpop (删除首值)rpop(删尾值)lpop key
rpop key
删除指定的值(删除几个):lrem lrem key count value
判断列表中成员个数:llen llen key
C、集合结构 --- set
value 部分是 set 集合
专属的命令:
存数据: sadd:sadd key value1...
取数据:smembers:smembers key
删除数据:随机删除 spop,指定要删除的值srem
spop key
srem key value
交集:sinter:sinter key1 key2
并集:sunion:sunion key1 key2
差集:sdiff:sdiff key1 key2
计算集合成员个数:scard :scard key
把数据从一个集合移动到另一个集合:smove:smove key1 key2 value
D、散列结构 -- hash 结构
Value 部分是 key=value
存散列结构数据:hset(一次向 value 部分放一对)、 hmset(一次向 value 部分 放多对)
获取散列结构数据:hget(一次获取一个)、hmget(一次获取多个)
获取所有的小 key 的名称:hkeys: hkeys key
获取所有的值:hvals: havals key
指定增量(整数、浮点数)hincrby、hincrbyfloat
hincrby bigkey littlekey value
hincrbyfloat bigkey littlekey value
从 hash 结构中删除一个小对:hdel:hedel bigkey littlekey...
获取小对的数量:hlen hlen key
E、有序集合 --- sorted set/zset
Value 部分也是小对;小对中的 key 部分必须是纯数字(score);小对中的 key 部分允许重复;小对中 value 部分不允许重复
zadd:zadd bigkey littlekey value...
zrange:zrange key startnum endnum
Redis 的相关命令
有序集合 -- zset/sorted set
1)专属命令:
a、添加:zadd: zadd key littlekey value...
b、升序获取:zrange: zrange key 0 -1
c、降序获取:zrevrange: zrevrange key 0 -1
d、指定分数区间获取:zrangebyscore
zrangescore key num lscore rscore
e、获取值对应的分数:zscore:zscore key littlekey
f、删除一小对:zrem:zrem key littlekey
注意:Redis能搭集群,但是Redis 是单线程,不进行多线程并行,事务可以可以把一段操作看成一个整体 开启与关闭事务
MULTI 标记开始 UNWATICH 标记结束
前提条件
1)远程连接条件:后台启动、防火墙释放端口、允许远程连接、可选密码设置(ssm 架构项目需要设置密码、springboot 架构项目不需要)
实现 java 中操作 Redis
1)创建项目、导入 jar 包 -- Java 和 Redis 连接的包 jedis
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10</version>
</dependency>
</dependencies>
2)实现测试:建立连接、完成功能、释放资源
ping 命令 测试是否连通成功 返回 pong
// 测试是否连通
@Test
public void testPing() {
// 建立连接
Jedis jedis = new Jedis("192.168.159.128",6379);
// 实现测试
String ping = jedis.ping();
System.out.println(ping);
// 释放资源
jedis.close();
}
// 向 Redis 0号库保存字符串结构数据
@Test
public void testSaveString() {
// 建立连接
Jedis jedis = new Jedis("192.168.159.128",6379);
// 保存字符串 默认0号库
jedis.set("name","zhangfei");
// 释放资源
jedis.close();
}
// 获取字符串结构数据
@Test
public void testGetString() {
// 建立连接
Jedis jedis = new Jedis("192.168.159.128",6379);
// 获取字符串 默认0号库
String name = jedis.get("name");
System.out.println(name);
// 释放资源
jedis.close();
}
// 向1号库保存 list 结构数据
@Test
public void testSaveList() {
// 建立连接
Jedis jedis = new Jedis("192.168.159.128",6379);
// 切换库
jedis.select(1);
// 保存集合
jedis.lpush("list1","bh1","bh3","bh2","bh5","bh1");
// 释放资源
jedis.close();
}
// 获取 list 结构数据
@Test
public void testGetList() {
// 建立连接
Jedis jedis = new Jedis("192.168.159.128",6379);
// 切换库
jedis.select(1);
// 获取集合
List<String> list1 = jedis.lrange("list1", 0, -1);
System.out.println(list1);
// 释放资源
jedis.close();
}
Java 中的对象如何向 Redis 保存和获取
Hash 散列
// 向 Redis 中保存实体类对象 -- 用 hash 散列结构存数据(保证是整体)
@Test
public void testPojoSave() {
// 创建对象(视图数据库)
User user = new User(1001,"张飞","123");
// 建立连接 (配置文件)
Jedis jedis = new Jedis("192.168.159.128",6379);
jedis.select(2);
Map<String,String> map = new HashMap<>();
map.put("uid",user.getUid()+"");
map.put("uname",user.getUname());
map.put("upwd",user.getUpwd());
jedis.hmset("user1",map);
// 释放资源
jedis.close();
}
// 从 Redis 库 获取数据给实体类对象
@Test
public void testPojoGet() {
// 建立连接 (配置文件)
Jedis jedis = new Jedis("192.168.159.128",6379);
jedis.select(2);
Map<String, String> map = jedis.hgetAll("user1");
User user = new User();
Set<String> strings = map.keySet();
for (String key : strings) {
if("uid".equals(key)) {
user.setUid(Integer.parseInt(map.get(key)));
}
if("uname".equals(key)) {
user.setUname(map.get(key));
}
if("upwd".equals(key)) {
user.setUpwd(map.get(key));
}
}
System.out.println(user);
jedis.close();
}
Json串 --- 常用方案
// 向 Redis 库中保存对象数据 -- 使用 json 串格式 -- 利用 Redis 中 string 结构
@Test
public void testPojoJson() throws JsonProcessingException {
// 创建对象(视图数据库)
User user = new User(1001,"张飞","123");
// 转换 json 串
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
// 建立连接 (配置文件)
Jedis jedis = new Jedis("192.168.159.128",6379);
jedis.select(2);
// 设置 json 串
jedis.set("user2",json);
// 释放资源
jedis.close();
}
// 从 Redis 库中获取 json 串,转回对象
@Test
public void testPojoGet2() throws IOException {
// 建立连接 (配置文件)
Jedis jedis = new Jedis("192.168.159.128",6379);
jedis.select(2);
String json = jedis.get("user2");
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue(json, User.class);
System.out.println(user);
jedis.close();
}
Redis 连接池
// 连接池操作
@Test
public void testPool() {
// 建立连接池
JedisPool jedisPool = new JedisPool("192.168.159.128",6379);
// 从连接池中获取连接对象
Jedis jedis = jedisPool.getResource();
// 实现功能
String ping = jedis.ping();
System.out.println(ping);
// 释放资源
jedis.close();
}
实现 Redis 的整合 -- 把 Redis 功能整合到项目中 -- 使用 Spring框架实现把 Redis 整合到项目中(池子对象在 IOC 容器管理)
1)最终就是实现使用 IOC 容器管理连接池对象即可
初始化配置 PoolConfig 参数 最大连接对象、初始化对象;超时时间等等
<bean class="redis.clients.jedis.JedisPool">
<!-- 池的属性 -->
<constructor-arg name="poolConfig" ref="poolConfig"/>
<!-- Redis主机地址 -->
<constructor-arg name="host" value="192.168.159.128"></constructor-arg>
<!-- Redis主机端口 -->
<constructor-arg name="port" value="6379"/>
<!-- Redis密码 -->
<constructor-arg name="password" value="123"/>
<!-- 可以选择Redis数据库(要选择数据库,必须开启了密码) -->
<constructor-arg name="database" value="0"/>
<!-- 链接超时时间 -->
<constructor-arg name="timeout" value="30000"/>
</bean>
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="10"/>
<!-- 空闲连接数 -->
<property name="maxIdle" value="2"/>
<!-- 设置链接池的连接耗尽时,是否等待 -->
<property name="blockWhenExhausted" value="true"/>
<!-- 最大等待时间 -->
<property name="maxWaitMillis" value="30000"/>
<!-- 获取到连接时,是否检查链接的有效性 -->
<property name="testOnBorrow" value="true"/>
</bean>
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class RedisTest {
@Autowired
private JedisPool pool;
@Test
public void test1() {
Jedis jedis = pool.getResource();
String ping = jedis.ping();
System.out.println(ping);
jedis.close();
}
}
Redis 加密码
vi redis.conf
搜索并增加 requirepass 123
本机登录 ./redis-cli -a 密码
Java连接
jedis.auth("密码");
缓存异常
1)缓存穿透
模拟多个请求、一直请求不存在的数据,给数据库压力太大(增加判断,防止恶意信息,布隆过滤器、增加空值的key)
2)缓存击穿
多线程访问同一个数据,请求缓存存在的数据,多个线程访问,但是数据在缓存有效时间到期了,同时穿过缓存向数据库发送多条SQL(热点数据,设置缓存永久有效,防止数据库同时访问量大,增加互斥锁)
3)缓存雪崩
同一时间缓存数据大面积失效(让不同缓存失效实现错开,增加互斥锁)
总结 -- 重点
1)基本数据结构/数据类型
2)常用命令
3)缓存异常