简介
redis是一种非关系型数据库,非结构化
在linux安装redis
安装Redis依赖
yum install -y gcc tcl
上传安装包并解压
上传安装包:直接将压缩文件拖到linux对应的文件夹中
tar -zxvf redis-7.0.11.tar.gz
启动
默认启动
redis-server
指定配置文件启动
#启动
redis-server redis.conf
#结束 2852为进程id
kill -9 2852
开机启动
vi /etc/systemd/system/redis.service
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-7.0.11/redis.conf
PrivateTmp=true
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable redis
# 启动
systemctl start redis
# 停止
systemctl stop redis
# 重启
systemctl restart redis
# 查看状态
systemctl status redis
# 查看redis进程命令
ps -ef | grep redis
Redis客户端
Redis命令行客户端
redis-cli [options] [commonds]
其中常见的options有:
-h 127.0.0.1:指定要连接的redis节点的IP地址,默认是127.0.0.1-p 6379:指定要连接的redis节点的端口,默认是6379-a 123321:指定redis的访问密码
其中commonds就是Redis的操作命令,例如:
ping:与redis服务端做心跳测试,服务端正常会返回pong
不指定cmmond时会进入redis-cli的交互控制台
图形化桌面客户端
RedisInsight下载地址:redis.com/thank-you/r…
下载Redis Desktop Manager
连接虚拟机redis
连接之前一定要把虚拟机的防火墙打开,把6379端口打开!!!!!!
systemctl start firewalld.service
firewall-cmd --add-port=6379/tcp --permanent
firewall-cmd --reload
Redis数据结构
Redis通用命令
通过help[command]可以查看一个命令的具体用法
- KEYS:查看符合模板的所有key
keys *
- EXISTS:查看key是否存在
EXISTS name
- EXPIRE:给一个key设置有效期,有效期到期该key自动删除
# 给key值为name设置20秒的有效期
EXPIRE name 20
- TTL:查看一个key的剩余有效期
- 当返回值为-1时,则永久有效
- 当返回值为-2时,则已经过期
TTL name
Redis的key格式
String类型
字符串类型 常见命令:
- SET:添加或修改已经存在的一个String类型的键值对
- GET:根据key获取String类型的value
- MSET:批量添加多个String类型的键值对
- MGET:根据多个key获取多个String类型的value
- INCR:让一个整型的key自增1
- INCRBY:让一个整型的key自增并指定步长,例如:incrby num 2 让num值自增2
- INCRBYFLOAT:让一个浮点型的数字自增并指定步长
- SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行
- SETEX:添加一个String类型的键值对,并指定有效期
Hash类型
也叫散列,其中value是一个无序字典,类似于java中的HashMap结构 常见命令:
- HSET key field value:添加或修改hash类型key的field的值
- HGET key field:获取一个hash类型key的field的值
- HMSET:批量添加多个hash类型key的field的值
- HMGET:批量获取多个hash类型key的field的值
- HGETALL:获取一个hash类型的key中的所有的field和value
- HKEYS:获取一个hash类型的key中的所有的field
- HVALS:获取一个hash类型的key中的所有的value
- HINCRBY:让一个hash类型key的字段值自增并指定步长
- HSETNX:添加一个hash类型的key的field值,前提是这个field不存在,否则不执行
List类型
类似于java中的LinkedList,可看作双向链表 特征与LinkedList相似:
- 有序
- 元素可重复
- 插入和删除快
- 查询速度慢
常见命令:
- LPUSH key element...:向列表左侧插入一个或多个元素
- LPOP key:移除并返回列表左侧的第一个元素,没有则返回nil
- RPUSH key element...:向列表右侧插入一个或多个元素
- RPOP key:移除并返回列表右侧第一个元素
- LRANGE key star end:返回一段角标范围内的所有元素
- BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil
Set类型
类似于java中的HashSet 特征:
- 无序
- 元素不可重复
- 查找快
- 支持交集、并集、差集等功能
常见命令:
- SADD key menber...:向set中添加一个或多个元素
- SREM key menber...:移除set中的指定元素
- SCARD key:返回set中元素的个数
- SISMEMBER key member:判断一个元素是否存在于set中
- SMEMBERS:获取set中所有的元素
- SINTER key1 key2...:求key1与key2的交集
- SDIFF key1 key2...:求key1与key2的差集
- SUNION key1 key2...:求key1和key2的并集
SortedSet类型
是一个可排序的set集合(常用于实现排行榜功能),与java中的TreeSet类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层实现是一个跳表(SkipList)加hash表 特征:
- 可排序
- 元素不重复
- 查询速度快
常用命令:
- ZADD key score menber:添加一个或多个元素到sorted set,如果已经存在则更新其score值
- ZREM key menber:删除sorter set中的一个指定元素
- ZSCORE key member:获取sorted set中的指定元素的score值
- ZRANK key member:获取sorted set中的指定元素的排名
- ZCARD key:返回sorted set中元素的个数
- ZCOUNT key min max:统计score值在给定范围内的所有元素的个数
- ZINCRBY key increment member:让sorted set中的指定元素自增,步长为指定的increment值
- ZRANGE key min max:按照score排序后,获取指定排名范围内的元素
- ZRANGEBYSCORE key min max:按照score排序后,获取指定score范围内的元素
- ZDIFF、ZINTER、ZUNION:求差集、交集、并集
Redis的Java客户端
Gitee地址
Jedis测试的gitee地址:gitee.com/LJJspider/r… SpringDataRedis测试的gitee地址:gitee.com/LJJspider/s…
Jedis引入和连接
- 引入依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
- 建立连接
public class JedisTest {
private Jedis jedis;
@BeforeEach
void setUp(){
//建立连接
jedis = new Jedis("192.168.1.5",6379);
//设置密码
jedis.auth("123321");
//选择库
jedis.select(0);
}
}
Jedis连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此使用连接池的方式
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisConnectionFactory {
private static final JedisPool jedisPool;
static {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//最大连接数
jedisPoolConfig.setMaxTotal(8);
//最大空闲连接
jedisPoolConfig.setMaxIdle(8);
//最小空闲连接
jedisPoolConfig.setMinIdle(0);
//设置最长等待时间 ms
jedisPoolConfig.setMaxWaitMillis(200);
jedisPool = new JedisPool(jedisPoolConfig,"127.0.0.1",6379,1000);
}
//获取Jedis对象
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
SpringDataRedis
- 引入依赖
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- common-pool -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
- 配置文件
spring:
redis:
host: 127.0.0.1
port: 6379
password: 123321
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 100
- 注入RedisTemplate
@Autowired
private RedisTemplate redisTemplate;
- 编写测试
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;
@SpringBootTest
class SpringDataRedisTestApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void testString() {
//写入一条String数据
redisTemplate.opsForValue().set("name","虎哥");
Object name = redisTemplate.opsForValue().get("name");
System.out.println(name);
}
}
RedisTemplate两种序列化方案
- 自定义RedisTemplate序列化,修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer
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.RedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
//创建RedisTemplate对象
RedisTemplate<String,Object> template = new RedisTemplate<>();
//设置连接工厂
template.setConnectionFactory(connectionFactory);
//创建JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//设置key的序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
//设置value的序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
//返回
return template;
}
}
- 使用StringRedisTemplate,手动序列化
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testSaveUser() throws JsonProcessingException {
//创建一个对象
User user = new User("胡歌",21);
//手动序列化
String json = mapper.writeValueAsString(user);
//写入一条String数据
stringRedisTemplate.opsForValue().set("user:200",json);
//获取数据
String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
//手动反序列化
User user1 = mapper.readValue(jsonUser,User.class);
System.out.println(user1);
}
缓存
数据交换的缓存区(Cache),是存储数据的临时地方,一般读写性能较高 缓存作用:
- 降低后端负载
- 提高读写效率,降低响应时间
缓存成本:
- 数据一致性成本
- 代码维护成本
- 运维成本
缓存更新
- 低一致性需求:使用Redis自带的内存淘汰机制
- 高一致性需求:主动更新,并以超时剔除作为兜底方案
- 读操作
- 缓存命中直接返回
- 缓存未命中则查询数据库,并写入缓存,设定超时时间
- 写操作
- 先写数据库,然后再删除缓存
- 要确保数据库与缓存操作的原子性
- 读操作
缓存雪崩
缓存雪崩是指同一时间内大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力 解决方案:
- 给不同的Key的TTL添加随机值
- 利用Redis集群提高服务的可用性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
缓存击穿
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的Key突然失效了, 无数的请求访问在瞬间给数据库带来巨大的冲击 解决方案:
- 互斥锁
- 逻辑过期
缓存穿透
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库 常见的解决方案:
- 缓存空对象
- 优点:实现简单,维护方便
- 缺点:可能造成短期的不一致
- 布隆过滤
- 优点:内存占用较少,没有多余key
- 缺点:实现复杂,存在误判可能
案例
短信登录
商户查询缓存
存在缓存穿透问题
解决缓存穿透问题
- 缓存空对象
解决缓存击穿问题
- 互斥锁解决缓存击穿
- 逻辑过期解决缓存击穿
优惠券秒杀
使用乐观锁对秒杀并发做处理
乐观锁和悲观锁
- 悲观锁:认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行
例如:Synchronized、Lock都是属于悲观锁
- 乐观锁:认为线程安全问题不一定发生,因此不加锁,只是在更新数据时去判断有没有其他线程对数据做修改
- 如果没有修改则认为是安全的,自己才更新数据
- 如果已经被其他线程修改,说明发生了安全问题,此时可以重试或异常