深入了解redis

一、常见的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-81918192-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时会自动封装了两个类:RedisTemplateStringRedisTemplate. 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();
     }
 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值