Redis系列

redis

一 Redis

1 NoSQL

介绍

NoSQL,泛指非关系型的数据库,NoSQL即Not-Only SQL,它可以作为关系型数据库的良好补充。

为什么学习NoSQL?

互联网产品的特点:

高并发(多人同时访问)、高性能(读写速度快)、高可用(万一某一台服务器挂掉了,还可以正常访问)、海量数据

传统的关系数据库就出现了性能和扩展的瓶颈,非关系型数据库应时而生,解决了互联网三高和海量数据的问题

NoSQL和关系型数据库是互补关系,在各自的应用场景都有自己特点,一般情况下我们使用关系数据库来持久化数据,对一切热点数据通过Nosql来作为缓存

主流产品

分类特点代表产品
键值存储数据一般存在内存中,读写速度快(10w/s),适合作为缓存服务 当成map即可redis
文档型数据库数据结构要求不严格,适合存储结构不确定或者价值较低的数据mongodb
列存储数据库查找速度快,更容易进行分布式扩展,适合作为文件存储服务Hbase
图形数据库使用“图结构”进行存储,适合做社交网络计算等等Neo4j

图形数据库介绍

https://blog.csdn.net/xlgen157387/article/details/79085901

redis是一种键值对的非关系型数据库

2 Redis介绍

​ Redis(Remote Dictionary Server)是用C语言开发的一个开源的高性能键值对数据库。它的所有数据都是保存在内存中的,这也就决定了其读写速度之快,是其它硬盘保存数据的系统所无法匹敌的。

官方曾经给出过一组测试数据,50个并发执行100000个请求: 读的速度是110000次/s,写的速度是81000次/s

3 Redis安装和使用

  • 下载
    Reids官网地址:http://redis.io
    中文网地址:https://www.redis.net.cn/
    GitHub地址:https://github.com/MSOpenTech/redis/tags

  • 安装
    windows版本解压缩即可

  • 目录(windows版)
    redis.windows.conf:配置文件
    redis-cli.exe:redis的客户端
    redis-server.exe:redis服务器端

  • 启动

    • 方式1 :

      • 先双击服务器端 redis-server.exe
      • 再双击客户端 redis-cli.exe
    • 方式2:在当前解压后的目录下 cmd进入dos窗口

      • 先启动服务器端 redis-server.exe redis.windows.conf
      • 再启动客户端 redis-cli.exe

      redis服务器默认的端口号:6379

      客户端连接其他服务器,需要在cmd中输入:

      • redis-cli.exe -h ip地址 -p 端口号
  • 关闭

    • 建议在客户端使用:shutdown
    • 也可以直接点 x

4 Redis数据结构【重点】

redis存储的是:key-value格式的数据,其中key是字符串类型,value支持五种数据类型(存到数据库中都是字符串)

  1. string
  2. hash (map)
  3. list (linkedlist)
  4. set (无序唯一)
  5. zset (sorted set 有序set)

二 redis命令操作

1 字符串(string)

添加 : set 键名 值

查询 : get 键名

删除 : del 键名

新增且设置时间 : se tex 键名 秒数 值

查看剩余存活时间 : ttl 键名

​ 若返回正整数,就是剩余存活时间

​ 若返回-1,永不过时

​ 若返回-2,服务器没有此键值对

扩展:

  • incr 键名 :自增

  • decr 键名 :自减

    可以作为数据库的主键的值

2 哈希(hash)

value是一个map

添加 : hset 键名 字段 子值

查询 : hget 键名 字段

删除 : hdel 键名 字段

扩展命令:

  • hmset 键名 字段1 子值1 字段2 子值2 :一次性给键设置多个字段和值
  • hmget 键名 字段1 字段2 :一次获取多个字段的值
  • hgetall 键名 :获取所有的字段和值

https://www.runoob.com/redis/redis-hashes.html

3 redis客户端安装使用

Another Redis Desktop Manager

4 列表(list)

LinkedList 有序

添加

  • lpush 键名 值1 值2 rpop
  • rpush 键名 值1 值2 值3 … 从右边开始一个一个的往末尾上加

查询 : lrange 键名 开始索引 结束索引

  • lrange 键名 0 -1 :查询所有

删除 :

  • lpop 键名 : 返回且移除左边的第一个元素
  • rpop 键名 : 返回且移除右边的第一个元素

扩展命令:

  • llen 键名 :获取集合的长度

list可以做队列使用, 左边加,右边弹

流量削峰

5 集合(set)

唯一无序

添加 : sadd 键名 值1 值2 值3

删除:srem 键名 值1 值2

查询

  • smembers 键名 : 查询所有
  • sismember 键名 值 : 判断该值是否存在

扩展命令:

  • 长度 : scard 键名
  • 集合运算 :
    • sunion 键1 键2 :并集 我有加你有
    • sdiff 键1 键2 :差集 我有你没有
    • sinter 键1 键2 :交集 我有你也有
    • 也可以将他们的运行结果放入一个新集合中
      • sxxxstore 新集合 键1 键2
      • 例如: sunionstore s3 s1 s2 将s1和s2的并集放入s3中

交集运算

6 有序集合(zset)

有序且唯一(sorted set)

添加 : zadd 键名 分数1 字段1 分数2 字段2

查询 : zrange 键名 开始索引 结束索引 [withscores]

查询某一个字段的分数 : zscore 键名 字段

删除 : zrem 键名 字段1 字段2

排行榜 zrevrange

7 通用命令

del 键名 : 删除指定key

keys * :查询当去数据库下所有的key

type 键名 : 查询该键的类型

exists 键名 : 查询该键是否存在当前的数据库中

扩展命令:

  • 一个redis实例包含16个数据库,默认使用的索引为0的数据库 : select 索引
    • 建议大家开发项目的时候 一个项目用一个实例
  • flushdb : 清除当前数据库中所有的内容
  • flushall : 清除当前实例中所有数据库中的内容

三 redis持久化

Redis的高性能是由于其将所有数据都存储在了内存中,为了使Redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。Redis支持两种方式的持久化(RDB和AOF),可以同时使用。

  • RDB(快照方式):默认方式.文件名称:dump.rdb

    rdb默认的快照规则:

    save 900 1 当15分钟之内有1个key发生变化就拍照

    save 300 10 当300秒之内有10个key发生变化就拍照

    save 60 10000 当60秒之内有1w个key发生变化就拍照

    重启服务器后,默认是从rdb文件中恢复数据到内存中

  • AOF(追加命令到文件中),需要手动开启的,文件名称:appendonly.aof

    appendfsync always 每执行一个命令,就会把命令追加到文件中,和关系型数据库相似

    appendfsync everysec 每一秒钟,把所有的操作命令追加到文件中

    appendfsync no 不同步,等同于rdb

    修改配置文件redis.windows.conf中 appendonly no 将他的值改成yes,启动的时候需要指定配置文件启动,重启服务器就生效,

    虽然开启了aof,但是rdb依然能用.只不过是,当服务器重启的时候,从aof文件将数据恢复到内存中.


当我们正常关闭redis服务器的时候,他会通过不同的方式将内存中的数据保存到硬盘文件中.再次启动的时候,会去文件中把数据恢复到内存中.

若我们把redis作为缓存使用,建议使用rdb方式,高效; 若数据需要不时的持久化,建议使用aof方式.

缓存是允许有数据丢失.

四 应用场景

  • 主键生成器, 使用string类型 ,使用incr操作让值进行自增即可
  • 缓存使用, 读取一些经常使用、不常修改且敏感度不强的数据的时候就可以将这些数据放入缓存中
    • 读取数据的时候优先去缓存中查找.缓存中有直接返回即可.缓存中若没有,就需要查询关系型数据库,查询到之后,把数据也放入到缓存中一份且返回.
    • 若缓存中关联的数据发生变更(修改或者删除),这时候就需要把redis中相关的数据清除掉.这样就可以保证关系型数据库中的数据和缓存中的数据是一致的.
  • 流量削峰(list类型的使用,作为队列使用)
  • 热点排行榜(zset类型,游戏排行榜,下载排行榜)

五 Jedis

1 什么是Jedis

Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、
Node.js、Go等。

在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推荐使用Jedis和Redisson。 在企业中用的最多的就是Jedis ,github下载地址:https://github.com/xetorthio/jedis

jedis对redis 相当于 jdbc对关系型数据库

2 快速入门

步骤分析

  1. 导入依赖
  2. 编码
    1. 使用构造器创建jedis对象(连接那个主机,端口号)
    2. 可以使用方法(redis中有那些命令就会有对应的方法)
    3. 释放资源
<!--jedis-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
public class TestJedis {
    @Test
    public void testHello(){
        //1. 使用构造器创建jedis对象(连接那个主机,端口号)
        Jedis jedis = new Jedis("127.0.0.1",6379);

        //2. 可以使用方法(redis中有那些命令就会有对应的方法)
        jedis.set("hi","jedis");

        String value = jedis.get("hi");//jedis
        System.out.println(value);

        String value_ = jedis.get("hello147");//null
        System.out.println(value_);

        //3. 释放资源
        jedis.close();
    }
}

常用API

方法解释
new Jedis(host, port)创建jedis对象,参数host是redis服务器地址,参数port是redis服务端口
set(key,value)设置字符串类型的数据
get(key)获得字符串类型的数据
hset(key,field,value)设置哈希类型的数据
hget(key,field)获得哈希类型的数据
lpush(key,values)设置列表类型的数据
lpop(key)列表左面弹栈
rpop(key)列表右面弹栈
del(key)删除指定的key
setex(key,秒数,value)设置一个key且设置存活时间

3 连接池

jedis连接资源的创建与销毁是很消耗程序性能,所以jedis为我们提供了jedis的池化技术。

步骤分析

  1. 导入依赖
  2. 编码
    1. 创建jedis连接池的配置对象
      • 配置连接信息
    2. 使用配置对象创建jedis连接池(JedisPool)
    3. 从连接池中获取jedis
    4. 使用jedis
    5. 用完之后千万别忘记归还
    @Test
    public void testPool(){
        //1. 创建jedis连接池的配置对象
        JedisPoolConfig config = new JedisPoolConfig();
        //   - 配置连接信息
        config.setMaxTotal(10);//最大连接数
        config.setMaxIdle(2);//最大空闲数

        //2. 使用配置对象创建jedis连接池(JedisPool)
        JedisPool pool = new JedisPool(config,"127.0.0.1",6379);

        //3. 从连接池中获取jedis
        Jedis jedis = pool.getResource();

        //4. 使用jedis
        jedis.set("heihei","pool");
        System.out.println(jedis.get("heihei"));

        //5. 用完之后千万别忘记归还
        jedis.close();
    }
    @Test
    public void testPool2(){
        //1. 创建jedis连接池的配置对象
        JedisPoolConfig config = new JedisPoolConfig();
        //   - 配置连接信息
        config.setMaxTotal(10);//最大连接数
        config.setMaxIdle(2);//最大空闲数
        config.setMaxWaitMillis(2000);//设置获取连接的超时时间

        //2. 使用配置对象创建jedis连接池(JedisPool)
        JedisPool pool = new JedisPool(config,"127.0.0.1",6379);

        //3. 从连接池中获取jedis 千万别忘记归还,否则过段时间就会报错
        for (int i = 0; i < 12; i++) {
            Jedis jedis = pool.getResource();
            System.out.println(jedis);
        }
    }

4 抽取工具类

jedis.properties

​ 放在main/resources目录下

jedis.host=127.0.0.1
jedis.port=6379
jedis.maxTotal=50
jedis.maxIdle=10

JedisUtils

public class JedisUtils {

    private static JedisPool jedisPool;

    //初始化jedis
    static {
        ResourceBundle bundle = ResourceBundle.getBundle("jedis");

        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(Integer.parseInt(bundle.getString("jedis.maxTotal")));
        config.setMaxIdle(Integer.parseInt(bundle.getString("jedis.maxIdle")));

        jedisPool = new JedisPool(config,bundle.getString("jedis.host"),Integer.parseInt(bundle.getString("jedis.port")));
    }

    //获取连接的方法
    public static Jedis getJedis(){
        return jedisPool.getResource();
    }
}

六 案例-优化导航条

1 需求:

分类导航条:之前我们在每个页面加载成功的时候都需要发送ajax请求从数据库中获取所有分类(list),这些数据轻易不改变,又不是敏感数据,经常要用.我们就可以把这些数据放入redis中

2 分析

  1. 使用redis进行优化
  2. 使用redis的string结构
  3. redis中key: category_list
  4. redis中value:list的集合的json字符串

2 步骤分析:

假如没有接口

3 代码实现:

public class CategoryApp {
    public String findAllFromRedis() throws JsonProcessingException {
        //1.先从redis中获取
        Jedis jedis = JedisUtils.getJedis();
        String listStr = jedis.get("category_list");

        //2.判断有无获取到值
        //2.1 若没有获取到,再从mysql中获取
        if (StrUtil.isBlank(listStr)) {
            SqlSession sqlSession = MybatisUtils.openSession();
            CategoryDao categoryDao = sqlSession.getMapper(CategoryDao.class);

            List<Category> list = categoryDao.findAll();

            MybatisUtils.commitAndClose(sqlSession);

            //需要将list集合转成json字符串放入redis中
            listStr = new ObjectMapper().writeValueAsString(list);
            jedis.set("category_list",listStr);

            System.out.println("从mysql中获取");
        }else{
            //2.2 若获取到了,直接返回
            System.out.println("从redis中获取");
        }

        //释放jedis
        jedis.close();

        //返回字符串
        return listStr;
    }
}


CategoryServlet

七 面向接口编程

没有使用面向接口编程遇到的一些问题

软件工程基本原则:高内聚,松耦合(低耦合)

  • 高内聚:就是软件或者模块或者类的内部要高度的聚集和关联

    例如:mybatis的查询操作.内部封装了获取连接,设置参数,执行sql,封装结果的操作

  • 松耦合:就是降低模块间的依赖关系

    例如:mybatis切换数据库的操作,只需要切换驱动类和sql即可.不需要修改mybatis的代码.


要想实现这种基本原则,在软件开发中就会有众多原则.今天我们来了解下开闭原则:

  • 开闭原则:对修改关闭 对扩展开发

    一个好的软件是在不修改源代码的情况下,可以扩展你的功能.而实现开闭原则的关键就是抽象化(使用接口或者抽象类).

案例优化下

提供一个CategoryService实现类

八 对象管理

上面案例依然存在的问题

1 工厂设计模式(能听懂即可)

举个现实生活的栗子:

tom需要一辆车:

方式1:自己买零件 组装

方式2:拿钱提需求,找汽车工厂帮我们提供这种车

java中

之前需要一个对象,使用new 创建出来;现在及以后需要一个对象,直接去工厂获取,工厂可以帮我们生产需要的对象

进入企业开发,我们不再手动 new 对象,都是从某一个工厂来获得

2 工厂实现(不需要写,只需要理解思想)

思路分析

步骤分析

  1. 编写一个mybeans.xml,放在src下
    • 将所有的service的实现类配置到此文件中(指定的格式)
  2. 编写一个工厂类 MyBeanFactory
    • 编写一个成员变量:map,用来存放所有的service实现类对象
    • 编写一个静态代码块:在工厂类加载的时候把xml文件进行加载且解析,创建配置好的所有的实现类对象,放入map中
    • 编写一个静态方法getBean:通过指定的key(id)去map中查询指定的实现类的对象返回

代码实现

编写myBeans.xml 配置文件

<beans>
    <bean id="userService" class="cn.itcast.service.impl.UserServiceImpl"/>
</beans>

编写MyBeanFactory类

public class MyBeanFactory {
    //1 编写一个成员变量:map,用来存放所有的service实现类对象
    private static Map<String,Object> map;

    //2 编写一个静态代码块:在工厂类加载的时候把xml文件进行加载且解析,创建配置好的所有的实现类对象,放入map中
    static {
        map = new HashMap<>();
        InputStream is = null;
        try {
            //以下代码只需要能看明白即可
            //a.加载配置文件
            is = MyBeanFactory1.class.getClassLoader().getResourceAsStream("mybeans.xml");
            //b.使用dom4j读取配置文件
            Document doc = new SAXReader().read(is);
            //c.使用xpath获取所有的bean标签 list集合
            List<Element> list = doc.selectNodes("/beans/bean");
            //d.遍历list集合,获取每个bean标签
            for (Element element : list) {
                //e.获取bean标签的id属性(作为map的key),获取class属性值,使用反射创建出对象(作为map的value),放入map中
                String id = element.attributeValue("id");
                String clazz = element.attributeValue("class");

                Object obj = Class.forName(clazz).newInstance();

                map.put(id,obj);
            }
            System.out.println("beanFactory 构建完毕,里面对象的个数为:"+map.size());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //3 编写一个静态方法getBean:通过指定的key(id)去map中查询指定的实现类的对象返回
    public static Object getBean(String id){
        return map.get(id);
    }
}

思考

目前配置文件是在第一次加载工厂类的时候加载的,且配置文件名字也是固定的.

这样第一次使用的人会特别慢,可以怎么办?

监听器:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vBAkAwuO-1621483835798)(redis.assets/image-20200626110636722.png)]

MyBeanFactory:

public class MyBeanFactory {
    private static Map<String,Object> map = new HashMap<>();

    public static void init(InputStream is){
        try {
            //使用dom4j+xpath 解析myBeans.xml 初始化map集合
           //2.获取document对象
            Document document = new SAXReader().read(is);

            //3.使用xpath获取所有的bean标签
            List<Element> list = document.selectNodes("/beans/bean");

            //4.遍历集合获取到每个bean标签
            for (Element element : list) {
                //5.获取每个bean标签的id属性
                String id = element.attributeValue("id");
                //6.获取每个bean标签的class属性
                String clazz = element.attributeValue("class");

                //7.使用反射技术创建class对象的实现类的对象
                Object obj = Class.forName(clazz).newInstance();

                //8.将id属性作为key,实现类对象作为value放入到map中
                map.put(id,obj);
            }

            System.out.println(map.size());
        } catch (DocumentException e) {
            e.printStackTrace();
            throw new RuntimeException("加载配置文件异常");
        } catch (IllegalAccessException |InstantiationException |ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException("解析配置文件时出现了异常");
        } finally {
            //释放资源
            if (is!=null) {
                try {
                    is.close();
                } catch (IOException e) {
                }
                is = null;
            }
        }
    }


    //使用id查询指定的实现类对象
    public static Object getBean(String id){
        return  map.get(id);
    }
}


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dJz6t2wz-1621483835799)(03.用户&面向接口编程&地址.assets/image-20200626110704704.png)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值