一、常见的redis的命令
1.1数据库的操作
flushdb //清空当前库的内容
flushall //清空所有库的内容
select 库名 // 选择库
1.2key的命令
keys* //查询当前库的所有key
del key key //删除指定key
exists key secondds //设置 key 的过期时间 ,单位为秒
ttl key //查看当前key的有效时间。-1 代表时间为永不过期
1.3字符串的命令
set key value //设置 key和value的值
get key //获取key值
mset key value key value //设置多个key和value
mget key key //获取多个key
incr key //自动增加value值 (1) 大都数用于点赞,收藏等功能
incrby key number //自动增加固定值
decr key //自动减少value值(1)
decr key number //。。。
1.4 hash类型的命令
hset key field value field value //给一个key 设置多个value值
hget key field //获取key值
hgetAll key //获取所有key值
hkeys key //获取hash中所有的field字段
hvals key //获取hash中所有的valus字段
1.5 list列表类型的命令
他的value为一个列表类型,里面的元素允许重复
lpush key value value value。。。//从左边开始存放列表数据
lpop key [count]// 从左边开始去除元素
lranfe key start end //从左边获取指定范围 ,-1:表示获取最后一个元素
1.6 set和sortedset类型的命令
1.6.1 set集合命令
redis中的valueshiset类型的特点是:无序且不重复
sadd key element element 。。//存放set类型的元素
smembers key // 获取指定key对应的所有元素
srandmember key [number] //获取一个key里面的一个或者多个值,主要看number的值
sinter key key : 求两个或者多个key的交集,如QQ里面的共同好友
spop key :随即移除一个或多个value值
1.6.2 sorted set集合命令
他和set集合的区别就是,添加元素时需要提供一个分数,该分数用来排序
zadd key element score element score //添加有序集合
zrange key start end //分数从小到大排列获取元素
zrevrange key stsrt end //返回有序集中指定空间的成员,通过索引,分数从高到低
二、 redis的应用场景
热点数据的缓存:减少对数据库的访问频率,提高应用程序的效率
限时业务的运用:如验证码的倒计时
计数器的相关问题:如bilibili的点赞,收藏等操作
排行榜的相关问题:如:销售量和观看量
分布式锁: 如syn自动锁,lock手动锁
打油诗:
记时计数和分布
热点数据的排行
2.1、redir的持久化应用
持久化就是把内存中的数据保存在磁盘的过程中,防止数据丢失
redir的两种持久化方法:
2.1.1、RDB快照模式
特点:每搁一段时间对redis内存中的数据进行拍照储存,他是redis默认的持久化方式
优点:数据恢复速度快
缺点:可能造成数据丢失,完整性差
RDB实现持久化有两种方法
(1)第一种是手动处理
save和bgsave
简单来说
save,保存文件时,会阻滞其他正在运行的项目,直到保存成功
bgsave,则会在保存文件时,在后台异步处理,另开一个线程,不会阻塞当前线程,
公司里面用的最多的也是这种方法
(2)第二种是自动处理
自动处理,需要修改redir的配置文件(底层还是使用的bgsave这种手动处理方法)
底层会自动判断在3600秒(一小时)内修改一次,就会自动保存一次
---手动和自动的方法,它们都会持久化(保存)到dump.rdb的配置文件里
2.1.2、AOF日志模式
会对每一个写命令通过write函数追加到日志文件中,当redir启动时,会读取日志文件中的命令,并把这些命令由上到下执行一遍
优点:数据完整好
缺点:数据回复慢
redir默认没有开启AOF模式,开启的话需要修改配置文件
2.2 redir集群模式
集群模式的配置原则:配次不配主
2.2.1、主从模式
顾名思义就是一个主节点,好多个次节点,
主节点具有读与写的功能,此节点则只有读的功能
因为在实际过程中,读的操作的请求比写的操作,要多的多,所以要将读的操作分配给多个次节点
修改三个配置文件
port 6380
dump6380.rdb
appendfilename "appendonly6380.aof"
启动三台redis配置文件
启动redir成功
ip:次节点的ip地址,因为是在同一台电脑上运行的也就是本地电脑运行的所以都是127.0.0.1
添加新的次节点,根据配置次不配主的原则
slaveof ip port
ip为 主节点的ip
port 为主节点的端口号
【不需要重启主节点服务】
2.2.2、哨兵模式
如果主从模式为1.0,那么哨兵模式就是2.0升级版
在主从模式上增加管理机器人(哨兵),负责监督主节点的问题,主节点一旦出现问题,那么项目就不能进行写的操作
如果主节点出现了问题【就需要工程师,处理问题,但是要是在半夜或者周围没有工程师】,那么设置的哨兵就会投票从次节点选出来一个新的主节点,代替之前的主节点,之前主节点修好了,自动成为此主节点的次节点
redis-sentinel sentinel.conf //启动哨兵
2.2.3、集群分片模式
集群分片模式:指在哨兵模式上的3.0加强版了。在哨兵模式中,用户的写的请求次数太大,致使主节点宕机,就会致使正在运行的程序,造成经济损失
结构图:
但是又会造成新的问题,这么多的请求该去那个主节点呢?
答:分槽技术: 默认的分槽数位是16384个,而每个槽点可以携带多个数据,底层会自动将这16384个槽点平均分配给所有主节点,加入有两个主节点,就会分成0-8191和8192-16384
两个阶段,客户发送来请求,会根据算法返回以一个数字,再根据这个数字对16384取余,
得到这个余数,对主节点的队列进行分配到对应的主节点
简略了解---
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
当你往Redis Cluster中加入一个Key时,会根据crc16(key) mod 16384计算这个key应该分布到哪个hash slot中,一个hash slot中会有很多key和value。你可以理解成表的分区,使用单节点时的redis时只有一个表,所有的key都放在这个表里;改用Redis Cluster以后会自动为你生成16384个分区表,你insert数据时会根据上面的简单算法来决定你的key应该存在哪个分区,每个分区里有很多key。
1、设置数据库的配置文件
单台电脑需要修改的内容
1、修改端口号(多台系统则不需要配置端口号)
2、修改rdb文件的名称(多台系统不需要修改)
3、必须开启aof模式并修改名称(默认是不开启的,多台系统不需要修改)
4、开启redis集群模式 cluster-enabled yes
5、修改集群文件名称 cluster-config-file nodes-7001.conf(多系统不需要修改)
6、设置允许任意ip访问:bind * -::*
2、根据配置文件开启redis的服务
3、启动redis服务
出现两行绿色文字就显示成功了
三、java连接redis
jedis是帮助java和redis建成连接,专业易用的一个客户端
3.1单机模式
3.1.1引入jedis依赖
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.0</version>
</dependency>
</dependencies>
3.1.2创建连接对象
测试前准备:虚拟机开启redis数据库
host:为开启的虚拟机的IP地址,
port:为设置的端口号
3.1.3可以直接调用里面的方法
3.1.4正常的执行操作
3.1.5执行成功
3.2集群模式
和单击模式只有连接的服务器的台数奴一样
四、stringboot连接redis
springboot在整合redis时会自动封装了两个类:RedisTemplate和StringRedisTemplate. StringRedisTemplate它是RedisTemplate的子类,
StringRedisTemplate它里面存储的key和value都是字符串类型
(1)引入redis启动依赖包
会完成自动装配功能。---完成RedisTemplate的创建以及容器管理。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
(2)修改配置文件
# 连接单机redis
spring.redis.host=自己的ip地址
spring.redis.port=端口号
(3)使用StringRedisTemplate
(4)使用RedisTemplate
因为每次都序列化就会太麻烦,就创建了RedisTemplate类对象并交于spring容器管理。
简化之后的类
(5)stringboot连接集群
配置文件和单机只有连接的配置文件不同
五、redis的实际应用
计时计数加分布
热点数据的排行
热点数据的缓存
可以将经常使用的数据存放在redis里面,以后在查询这些数据时,就可以优先从redis里面查询了
如果redis里面没有则会再查寻数据库,并将查询的数据库的数据,放入redis里面
适合放入redis的数据
1. 查询频率高的数据
2. 修改频率低的数据
3. 数据安全性要求不高的
数据放在redis的优点:
1.提高查询效率
2.降低数据库的访问频率,减少数据库的压力
5、1如何设置redis作为缓存
1、添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2、配置文件
#数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/qy168?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
#redis
spring.redis.host=自己的IP
spring.redis.port=6379
#默认的地址
#mybatis-plus
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
3、实体类、接口和service
//实现类
@Data
@TableName("tbl_dept")//网址路径
public class Dept {
@TableId(type = IdType.AUTO)
private Integer did;
@TableField(value = "d_name")
private String dname;
private String loc;
}
//接口
public interface DeptDao extends BaseMapper<Dept> {
}
//service层 继承了DeptServie类
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptDao deptDao;
@Override
public Dept findById(Integer id) {
Dept dept = deptDao.selectById(id);
return dept;
}
}
如果有多个请求,就会频繁访问数据库,造成浪费
解决:引入缓存。访问次数多的数据,就将其放入缓存内,不在访问数据库
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptDao deptDao;
@Autowired
private RedisTemplate redisTemplate;
@Override
public Dept findById(Integer id) {
ValueOperations valueOperations = redisTemplate.opsForValue();
//1.查询缓存
Object o = valueOperations.get("dept::" + id);
//2.判断是否缓存命中
if(o!=null&&o instanceof Dept){//o instanceof Dept: 判断对象是否属于Dept类型
return (Dept) o;
}
//3. 缓存没有命中则查询数据库
Dept dept = deptDao.selectById(id);
if(dept!=null){
//4. 查询的数据存入缓存中以便下次能从缓存中获取
valueOperations.set("dept::" + id,dept);
}
return dept;
}
//添加,修改和删除
@Override
@Transactional
public int delete(Integer id) {
int row = deptDao.deleteById(id);
redisTemplate.delete("dept::"+id);
return row;
}
@Override
public Dept insert(Dept dept) {
int insert = deptDao.insert(dept);
return dept;
}
@Override
public Dept update(Dept dept) {
redisTemplate.opsForValue().set("dept::"+dept.getDid(),dept);
int i = deptDao.updateById(dept);
return dept;
}
}
如果在使用redis作为缓存时,每次都需要自己添加缓存代码。未来维护时还要维护redis非业务代码
解决:可以使用AOP(日志功能)解决
【spring框架也会想到使用AOP决绝业务代码和缓存的非业务代码的重合--缓存的注解】
操作:开启spring缓存注解
@SpringBootApplication
@MapperScan(basePackages = "com.ykq.dao")
@EnableCaching //开启缓存注解驱动
@EnableTransactionManagement //开启事务注解驱动
public class Qy168SpringbootRedis02Application {
public static void main(String[] args) {
SpringApplication.run(Qy168SpringbootRedis02Application.class, args);
}
}
5、2使用redis完成分布式锁
一台代理服务器,两台运行服务器
当有很多请求同时访问两个程序服务器时,就会造成数据穿透,所以需要加一个锁,只有里面的请求处理完成时,才可以进行下一个请求
启动两台程序
配置nginx文件
5、3redis实现分布式锁的缺陷
当程序执行时间超过redis锁的时间时,会出现死锁或者bug
如何解决--使用第三方插件---》redisson提供的一个”看门狗机制“
(1)添加依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.23.4</version>
</dependency>
(2)创建一个RedissonClient对象--交给spring容器管理
@Bean //返回的对象交于spring容器来管理
public RedissonClient redissonClient(){
Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.223.158:6379");
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
(3)使用RedissonClient对象
@Autowired
private RedissonClient redissonClient;
public String decrement(Integer productid) {
//获取指定的锁对象
RLock rlock = redissonClient.getLock("product::" + productid);
//加锁
rlock.lock(30,TimeUnit.SECONDS);
try {
//如果返回true表示获取锁成功,返回的是false表示获取锁失败
int num = stockDao.findById(productid);
if (num > 0) {
stockDao.update(productid);
System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
} else {
System.out.println("商品编号为:" + productid + "的商品库存不足。");
return "商品编号为:" + productid + "的商品库存不足。";
}
}finally {
//释放锁资源
rlock.unlock();
}
}