Redis的简单介绍及其使用

1 Redis介绍

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

原子性说明: Redis的操作是单进程单线程操作,所以没有线程并发性的安全问题. 采用队列的方式一个一个操作.
Redis常见用法:
1.Redis可以当做缓存使用
2.Redis可以当做数据库使用 验证码
3.Redis可以消息中间件使用 银行转账等

2 缓存机制

说明:使用缓存可以有效的降低用户访问物理设备的频次.快速从内存中获取数据,之后返回给用户,同时需要保证内存中的数据就是数据库数据.
思考:
1.缓存的运行环境应该在内存中.(快)
2.使用C语言开发缓存
3.缓存应该使用什么样的数据结构呢--------K-V结构 一般采用String类型居多 key必须唯一 . v:JSON格式
4.内存环境断电即擦除,所以应该将内存数据持久化(执行写盘操作)
5.如果没有维护内存的大小,则容易导致 内存数据溢出. 采用LRU算法优化!!!

3 Redis安装

1).解压 Redis安装包

[root@localhost src]# tar -zxvf redis-5.0.4.tar.gz

在这里插入图片描述

2). 安装Redis
说明:在Redis的根目录中执行命令
命令: 1.make
2.make install
在这里插入图片描述

4 修改Redis的配置文件

命令1: 展现行号 :set nu
在这里插入图片描述

修改位置1: 注释IP绑定
在这里插入图片描述

修改位置2: 关闭保护模式
在这里插入图片描述

修改位置3: 开启后台启动
在这里插入图片描述

5 redis 服务器命令

1.启动命令: redis-server redis.conf
2.检索命令: ps -ef | grep redis
3.进入客户端: redis-cli -p 6379
4.关闭redis: kill -9 PID号 | redis-cli -p 6379 shutdown
在这里插入图片描述

6 Redis入门案例

6.1 引入jar包文件

<!--spring整合redis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
        </dependency>

6.2 编辑测试API

package com.jt.test;

import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;

public class TestRedis {

    /**
     * 1.测试redis程序链接是否正常
     * 步骤:
     *      1.实例化jedis工具API对象(host:port)
     *      2.根据实例 操作redis  方法就是命令
     *
     * 关于链接不通的说明:
     *      1.检查Linux防火墙
     *      2.检查Redis配置文件修改项
     *          2.1 IP绑定
     *          2.2 保护模式
     *          2.3 后台启动
     *      3.检查redis启动方式  redis-server redis.conf
     *      4.检查IP 端口 及redis是否启动...
     *
     *      */
    @Test
    public void test01(){
        String host = "192.168.126.129";
        int port = 6379;
        Jedis jedis = new Jedis(host,port);
        jedis.set("cgb2006","好好学习 天天向上");
        System.out.println(jedis.get("cgb2006"));

        //2.练习是否存在key
        if(jedis.exists("cgb2006")){
            jedis.del("cgb2006");
        }else{
            jedis.set("cgb2006", "xxxx");
            jedis.expire("cgb2006", 100);
        }


    }
}

6.3 Redis常见用法

6.3.1 setex学习

/**
     * 2.需求:
     *      1.向redis中插入数据  k-v
     *      2.为key设定超时时间  60秒后失效.
     *      3.线程sleep 3秒
     *      4.获取key的剩余的存活时间.
     *
     *   问题描述: 数据一定会被删除吗??????
     *   问题说明: 如果使用redis 并且需要添加超时时间时 一般需要满足原子性要求.
     *   原子性:   操作时要么成功 要么失败.但是必须同时完成.
     */
    @Test
    public void test02() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.setex("宝可梦", 60, "小火龙 妙蛙种子");
        System.out.println(jedis.get("宝可梦"));

       /* Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.set("宝可梦", "小火龙 妙蛙种子");
        int a = 1/0;    //可能会出异常
        jedis.expire("宝可梦", 60);
        Thread.sleep(3000);
        System.out.println(jedis.ttl("宝可梦"));*/
    }

6.3.2 setnx

/**
     * 3.需求如果发现key已经存在时 不修改数据.如果key不存在时才会修改数据.
     *
     */
    @Test
    public void test03() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.setnx("aaaa", "测试nx的方法");
        /*if(jedis.exists("aaa")){
            System.out.println("key已经存在 不做修改");
        }else {
            jedis.set("aaa", "测试数据");
        }*/
        System.out.println(jedis.get("aaaa"));
    }

6.3.3 set 超时时间原子性操作

/**
     * 需求:
     *  1.要求用户赋值时,如果数据存在则不赋值.  setnx
     *  2.要求在赋值操作时,必须设定超时的时间 并且要求满足原子性 setex
     */
    @Test
    public void test04() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        SetParams setParams = new SetParams();
        setParams.nx().ex(20);
        jedis.set("bbbb", "实现业务操作AAAA", setParams);
        System.out.println(jedis.get("bbbb"));


    }

6.3.4 list集合练习

  @Test
    public void testList() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.lpush("list", "1","2","3");
        System.out.println(jedis.rpop("list"));
    }

6.3.5 redis事务控制

 @Test
    public void testTx() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        //1.开启事务
        Transaction transaction = jedis.multi();
        try {
            transaction.set("aa", "aa");
            //提交事务
            transaction.exec();
        }catch (Exception e){
            e.printStackTrace();
            //回滚事务
            transaction.discard();
        }
    }

7 秒杀业务逻辑—分布式锁机制

6988 —1块 1部手机 20显示抢购成功 并且支付了1块钱…
问题:
1.tomcat服务器有多台
2.数据库数据只有1份
3.必然会出现高并发的现象.
如何实现抢购…

7.1常规锁操作

7.1.1 超卖的原因

在这里插入图片描述

7.1.2 同步锁的问题

说明:同步锁只能解决tomcat内部的问题,不能解决多个tomcat并发问题
在这里插入图片描述

7.1.3 分布式锁机制

思想:
1.锁应该使用第三方操作 ,锁应该公用.
2.原则:如果锁被人正在使用时,其他的用户不能操作.
3.策略: 用户向redis中保存一个key,如果redis中有key表示有人正在使用这把锁 其他用户不允许操作.如果redis中没有key ,则表示我可以使用这把锁.
4.风险: 如何解决死锁问题. 设定超时时间.
在这里插入图片描述

8 SpringBoot整合Redis

8.1 编辑配置文件 redis.pro

说明:由于该配置被其他的项目共同使用,则应该写到common中.
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020110123465149.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxOTE0ODk5,size_16,color_FFFFFF,t_70#pic_center

8.2 编辑配置类

说明: 编辑redis配置类.将Jedis对象交给Spring容器进行管理.

@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {

    @Value("${redis.host}")
    private String host;
    @Value("${redis.port}")
    private Integer port;

    @Bean
    public Jedis jedis(){

        return new Jedis(host,port);
    }
}

8.3 对象与JSON转化 ObjectMapper介绍

8.3.1 简单对象转化

/**
     * 测试简单对象的转化
     */
    @Test
    public void test01() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(100L).setItemDesc("商品详情信息")
                .setCreated(new Date()).setUpdated(new Date());
        //对象转化为json
        String json = objectMapper.writeValueAsString(itemDesc);
        System.out.println(json);

        //json转化为对象
        ItemDesc itemDesc2 = objectMapper.readValue(json, ItemDesc.class);
        System.out.println(itemDesc2.getItemDesc());
    }

8.3.2 集合对象转化

/**
     * 测试集合对象的转化
     */
    @Test
    public void test02() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(100L).setItemDesc("商品详情信息1")
                .setCreated(new Date()).setUpdated(new Date());
        ItemDesc itemDesc2 = new ItemDesc();
        itemDesc2.setItemId(100L).setItemDesc("商品详情信息2")
                .setCreated(new Date()).setUpdated(new Date());
        List<ItemDesc> lists = new ArrayList<>();
        lists.add(itemDesc);
        lists.add(itemDesc2);
        //[{key:value},{}]
        String json = objectMapper.writeValueAsString(lists);
        System.out.println(json);

        //将json串转化为对象
        List<ItemDesc> list2 = objectMapper.readValue(json, lists.getClass());
        System.out.println(list2);
    }

8.4 编辑工具API

package com.jt.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jt.pojo.ItemDesc;
import org.springframework.util.StringUtils;

public class ObjectMapperUtil {

    /**
     * 1.将用户传递的数据转化为json串
     * 2.将用户传递的json串转化为对象
     */
    private static final ObjectMapper MAPPER = new ObjectMapper();

     //1.将用户传递的数据转化为json串
    public static String toJSON(Object object){

        if(object == null) {
            throw new RuntimeException("传递的数据为null.请检查");
        }

        try {
            String json = MAPPER.writeValueAsString(object);
            return json;
        } catch (JsonProcessingException e) {
            //将检查异常,转化为运行时异常
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    //需求: 要求用户传递什么样的类型,我返回什么样的对象  泛型的知识
    public static <T> T toObj(String json,Class<T> target){
        if(StringUtils.isEmpty(json) || target ==null){
            throw new RuntimeException("参数不能为null");
        }
        try {
           return  MAPPER.readValue(json, target);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

8.5 商品分类的缓存实现

8.5.1 实现步骤

1.定义Redis中的key key必须唯一不能重复. 存 取 key = “ITEM_CAT_PARENTID::70”
2.根据key 去redis中进行查询 有数据 没有数据
3.没有数据 则查询数据库获取记录 之后将数据保存到redis中 方便后续使用.
4.有数据 表示用户不是第一次查询 可以将缓存数据直接返回即可.

8.5.2 编辑ItemCatController

在这里插入图片描述

8.5.3 编辑ItemCatService

@Override
    public List<EasyUITree> findItemCatListCache(Long parentId) {
        //0.定义公共的返回值对象
        List<EasyUITree> treeList = new ArrayList<>();
        //1.定义key
        String key = "ITEM_CAT_PARENTID::"+parentId;
        //2.检索redis服务器,是否含有该key

        //记录时间
        Long startTime = System.currentTimeMillis();
        if(jedis.exists(key)){
            //数据存在
            String json = jedis.get(key);
            Long endTime = System.currentTimeMillis();
            //需要将json串转化为对象
            treeList = ObjectMapperUtil.toObj(json,treeList.getClass());
            System.out.println("从redis中获取数据 耗时:"+(endTime-startTime)+"毫秒");
        }else{
            //3.数据不存在  查询数据库
            treeList = findItemCatList(parentId);
            Long endTime = System.currentTimeMillis();
            //3.将数据保存到缓存中
            String json = ObjectMapperUtil.toJSON(treeList);
            jedis.set(key, json);
            System.out.println("查询数据库 耗时:"+(endTime-startTime)+"毫秒");
        }
        return treeList;
    }

8.5.4 使用Redis的速度差

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis中的set是一种无序、唯一的数据结构,其主要使用场景包括: 1. 去重:set中存储的元素是唯一的,可以用于去重操作。例如,可以将日志中所有的IP地址存储到set中,然后使用SMEMBERS命令获取所有的不重复IP地址。 2. 标记:set可以用于标记某个元素是否存在。例如,可以将文章的所有标签存储到set中,然后使用SISMEMBER命令判断某个标签是否存在。 3. 集合运算:set支持交、并、差等集合运算,可以方便地进行集合操作。例如,可以将用户的关注列表存储在一个set中,将粉丝列表存储在另一个set中,然后使用SINTER命令获取共同关注的用户,使用SDIFF命令获取只关注A而不关注B的用户。 下面是一些具体的使用场景和操作命令: 1. 用户关注列表和粉丝列表: 将用户A的关注列表存储到名为following:A的set中,将用户A的粉丝列表存储到名为followers:A的set中。 ``` SADD following:A B C D SADD followers:A X Y Z ``` 获取用户A的关注列表和粉丝列表: ``` SMEMBERS following:A SMEMBERS followers:A ``` 获取用户A和用户B的共同关注列表: ``` SINTER following:A following:B ``` 获取只关注A而不关注B的用户列表: ``` SDIFF following:A following:B ``` 2. 标签统计: 将文章的标签存储到名为tags:article_id的set中,使用SADD命令添加标签,使用SCARD命令获取标签数。 ``` SADD tags:1001 python redis SADD tags:1002 python mongodb ``` 获取文章1001的标签数: ``` SCARD tags:1001 ``` 获取所有文章的标签统计: ``` SUNIONSTORE all_tags tags:* ``` 获取所有标签及其出现次数: ``` ZUNIONSTORE tag_counts 2 all_tags all_tags WEIGHTS 1 1 AGGREGATE SUM ZRANGE tag_counts 0 -1 WITHSCORES ``` 3. 数据去重: 将日志中的IP地址存储到名为ips的set中,使用SADD命令添加IP地址,使用SCARD命令获取去重后的IP数。 ``` SADD ips 192.168.1.1 192.168.1.2 192.168.1.1 ``` 获取去重后的IP数: ``` SCARD ips ``` 注意,以上只是一些简单使用场景和操作命令,实际应用中还需要根据具体需求进行设计和使用

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值