Redis的学习

Updated on with 0 views and 0 comments

Redis的学习

Redis

Redis介绍

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。

  1. 基于内存存储的、以key = value结构存储数据、开源的、可以用于实现数据库、缓存、消息中间件的系统(从数据库角度,类似mysql,但是没有表结构,是键值对)

  2. 数据结构,以哪些结构存储数据?基本结构:字符串结构、列表结构、集合结构、有序集合结构

  3. 有事务管理、可以实现数据复制、可以实现数据的持久化、可以实现 LUA 的脚本操作

  4. Redis 有高可用、高性能特点:采用集群保证高可用和高性能,在实现集群的时候设置了哨兵的功能。

    Tomcat 的并发(同时访问一条连接) 200条 超过300宕机,需要使用集群。(搭建多个服务器,每个都相同 用户随机访问)

    分布式:不同功能分不同的模块,加上集群,一堆相同的登录、相同的评论等等等。一堆相同的是一个小集群,小集群与小集群是分布式。

    哨兵:用来监测集群的某个服务有没有宕机,是否正常工作,存在主服务。

    端口:6379

Redis 安装

  1. 下载: http://download.redis.io/releases/

  2. 解压: tar -xzvf

  3. 安装:make PREFIX=/usr/app/software/redis install

    CentOS默认没有安装gcc,这会导致我们无法make成功。使用yum安装:

    yum -y install gcc

  4. 启动

    • 前端启动方式

      通过 ./redis-server 启动 由于不能实现交互 常采用后台启动方式

    • 后端启动方式

      A、到解压文件夹中复制 redis.conf 文件到 安装目录的 bin 文件夹中

      B、vi redis.conf 找 (/来进行搜索/daemonize no 改为 yes)

      C、redis 先停止服务,在启动,

      通过 ./redis-server redis.conf

Redis 的客户端登录操作

  1. 自带的客户端

    安装目录中 bin 文件夹里面的 redis-cli

    ./redis-cli 启动

  2. 其他客户端工具

  3. 在 Java 程序中操作 -- 如果是远程连接

    A、后台启动方式

    B、把端口防火墙释放

    C、运行远程连接

    D、可选的密码设置

Redis 的操作 -- 相关命令

  1. 全局性质的命令(是指不分数据结构,针对所有的数据结构都可以实现操作)

    A、停止服务器:shutdown [NOSAVE|SAVE] 持久化与非持久化

    B、退出客户端登录:exit/quit(当直接退出客户端登录,会自动持久化)(关机 内存->硬盘;开机 硬盘->内存)

    C、Redis 中默认有16个库,这些库从0-15 编号,客户端登录时,默认操作的是0号库;切换仓库的操作 select n(0号库没有数字提示)

    D、可以设置数据的有效时间 expire key time

    存储数据 set key value

    E、查看数据的剩余有效时间,ttl key

    看到 -1 表示永久有效 -2 表示原来有 但是删除了

    有效期到来自动删除 正值表示剩余有效时间

    F、删除数据 del key

    G、查看当前库中有哪些key keys *

    H、把当前库的所有数据都删除 flushdb

    I、把所有库的所有数据都删除 flushall

    J、查看数据所属的结构/类型 type key

    K、判断当前库中是否存在某个键值对数据 exists key 1代表在 0代表无

  2. 局部性质的命令(是指不同的数据结构,操作命令不同)

    A、字符串结构:string

    Value 部分整体就是一个字符串

    专属命令:

    存字符串结构的数据:set(一次只能存一对,当前库无同名 key、如果已经有同名 key,则替换操作)、mset(一次可以存多对)、取字符串结构的数据:get(一次只能获取一个值)、mget(一次可以获取多个值)

    向 value 部分追加新串:append key value

    计算 value 部分字符串的长度: strlen key

    截取字串: substr key startnum endnum

    当 value 部分是纯数字时,可以实现值的递增或者递减操作

    增/减1操作: incr key decr key

    指定增/减量:incrby key num decrby key num

    B、列表结构 --- list

    Value 部分是 list 列表(有序、可重复、下标访问)

    专属的命令:

    存列表结构的数据:lpush、rpush

    lpush key value1... rpush key value1...

    取列表结构的数据:lrange: lrange key startnum endnum endnum 取-1 也是所有

    下标访问:lindex lindex key index

    删除两端数据:lpop (删除首值)rpop(删尾值)lpop key rpop key

    删除指定的值(删除几个):lrem lrem key count value

    判断列表中成员个数:llen llen key

    C、集合结构 --- set

    value 部分是 set 集合

    专属的命令:

    存数据: sadd:sadd key value1...

    取数据:smembers:smembers key

    删除数据:随机删除 spop,指定要删除的值srem

    spop key srem key value

    交集:sinter:sinter key1 key2

    并集:sunion:sunion key1 key2

    差集:sdiff:sdiff key1 key2

    计算集合成员个数:scard :scard key

    把数据从一个集合移动到另一个集合:smove:smove key1 key2 value

    D、散列结构 -- hash 结构

    Value 部分是 key=value

    存散列结构数据:hset(一次向 value 部分放一对)、 hmset(一次向 value 部分 放多对)

    获取散列结构数据:hget(一次获取一个)、hmget(一次获取多个)

    获取所有的小 key 的名称:hkeys: hkeys key

    获取所有的值:hvals: havals key

    指定增量(整数、浮点数)hincrby、hincrbyfloat

    hincrby bigkey littlekey value

    hincrbyfloat bigkey littlekey value

    从 hash 结构中删除一个小对:hdel:hedel bigkey littlekey...

    获取小对的数量:hlen hlen key

    E、有序集合 --- sorted set/zset

    Value 部分也是小对;小对中的 key 部分必须是纯数字(score);小对中的 key 部分允许重复;小对中 value 部分不允许重复

    zadd:zadd bigkey littlekey value...

    zrange:zrange key startnum endnum

Redis 的相关命令

  1. 有序集合 -- zset/sorted set

    1)专属命令:

    a、添加:zadd: zadd key littlekey value...

    b、升序获取:zrange: zrange key 0 -1

    c、降序获取:zrevrange: zrevrange key 0 -1

    d、指定分数区间获取:zrangebyscore

    zrangescore key num lscore rscore

    e、获取值对应的分数:zscore:zscore key littlekey

    f、删除一小对:zrem:zrem key littlekey

    注意:Redis能搭集群,但是Redis 是单线程,不进行多线程并行,事务可以可以把一段操作看成一个整体 开启与关闭事务

    MULTI 标记开始 UNWATICH 标记结束

Java 操作 Redis

  1. 前提条件

    1)远程连接条件:后台启动、防火墙释放端口、允许远程连接、可选密码设置(ssm 架构项目需要设置密码、springboot 架构项目不需要)

  2. 实现 java 中操作 Redis

    1)创建项目、导入 jar 包 -- Java 和 Redis 连接的包 jedis

    <dependencies>
    
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.9.RELEASE</version>
        </dependency>
    
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.9.RELEASE</version>
        </dependency>
    
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.6.0</version>
        </dependency>
    
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.10</version>
        </dependency>
    </dependencies>
    

    2)实现测试:建立连接、完成功能、释放资源

    ping 命令 测试是否连通成功 返回 pong

     // 测试是否连通
        @Test
        public void testPing() {
    
            // 建立连接
            Jedis jedis = new Jedis("192.168.159.128",6379);
    
            // 实现测试
            String ping = jedis.ping();
            System.out.println(ping);
    
            // 释放资源
            jedis.close();
        }
    
    // 向 Redis 0号库保存字符串结构数据
        @Test
        public void testSaveString() {
    
            // 建立连接
            Jedis jedis = new Jedis("192.168.159.128",6379);
    
            // 保存字符串 默认0号库
            jedis.set("name","zhangfei");
    
            // 释放资源
            jedis.close();
        }
    
        // 获取字符串结构数据
        @Test
        public void testGetString() {
    
            // 建立连接
            Jedis jedis = new Jedis("192.168.159.128",6379);
    
            // 获取字符串 默认0号库
            String name = jedis.get("name");
            System.out.println(name);
    
            // 释放资源
            jedis.close();
        }
    
        // 向1号库保存 list 结构数据
        @Test
        public void testSaveList() {
    
            // 建立连接
            Jedis jedis = new Jedis("192.168.159.128",6379);
    
            // 切换库
            jedis.select(1);
    
            // 保存集合
            jedis.lpush("list1","bh1","bh3","bh2","bh5","bh1");
    
            // 释放资源
            jedis.close();
        }
    
        // 获取 list 结构数据
        @Test
        public void testGetList() {
    
            // 建立连接
            Jedis jedis = new Jedis("192.168.159.128",6379);
    
            // 切换库
            jedis.select(1);
    
            // 获取集合
            List<String> list1 = jedis.lrange("list1", 0, -1);
            System.out.println(list1);
    
            // 释放资源
            jedis.close();
        }
    
  3. Java 中的对象如何向 Redis 保存和获取

    Hash 散列

    // 向 Redis 中保存实体类对象 -- 用 hash 散列结构存数据(保证是整体)
        @Test
        public void testPojoSave() {
    
            // 创建对象(视图数据库)
            User user = new User(1001,"张飞","123");
    
            // 建立连接 (配置文件)
            Jedis jedis = new Jedis("192.168.159.128",6379);
    
            jedis.select(2);
    
            Map<String,String> map = new HashMap<>();
    
            map.put("uid",user.getUid()+"");
            map.put("uname",user.getUname());
            map.put("upwd",user.getUpwd());
    
            jedis.hmset("user1",map);
    
            // 释放资源
            jedis.close();
        }
    
    // 从 Redis 库 获取数据给实体类对象
        @Test
        public void testPojoGet() {
            // 建立连接 (配置文件)
            Jedis jedis = new Jedis("192.168.159.128",6379);
    
            jedis.select(2);
    
            Map<String, String> map = jedis.hgetAll("user1");
    
            User user = new User();
    
            Set<String> strings = map.keySet();
    
            for (String key : strings) {
    
                if("uid".equals(key)) {
                    user.setUid(Integer.parseInt(map.get(key)));
                }
                if("uname".equals(key)) {
                    user.setUname(map.get(key));
                }
                if("upwd".equals(key)) {
                    user.setUpwd(map.get(key));
                }
            }
    
            System.out.println(user);
    
    
            jedis.close();
        }
    

    Json串 --- 常用方案

        // 向 Redis 库中保存对象数据 -- 使用 json 串格式 -- 利用 Redis 中 string 结构
        @Test
        public void testPojoJson() throws JsonProcessingException {
    
            // 创建对象(视图数据库)
            User user = new User(1001,"张飞","123");
    
            // 转换 json 串
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(user);
    
            // 建立连接 (配置文件)
            Jedis jedis = new Jedis("192.168.159.128",6379);
    
            jedis.select(2);
    
            // 设置 json 串
            jedis.set("user2",json);
    
            // 释放资源
            jedis.close();
    
        }
    
        // 从 Redis 库中获取 json 串,转回对象
        @Test
        public void testPojoGet2() throws IOException {
    
            // 建立连接 (配置文件)
            Jedis jedis = new Jedis("192.168.159.128",6379);
    
            jedis.select(2);
    
            String json = jedis.get("user2");
    
            ObjectMapper objectMapper = new ObjectMapper();
            User user = objectMapper.readValue(json, User.class);
            System.out.println(user);
            jedis.close();
    
        }
    
  4. Redis 连接池

        // 连接池操作
        @Test
        public void testPool() {
    
            // 建立连接池
            JedisPool jedisPool = new JedisPool("192.168.159.128",6379);
    
            // 从连接池中获取连接对象
            Jedis jedis = jedisPool.getResource();
    
            // 实现功能
            String ping = jedis.ping();
            System.out.println(ping);
    
            // 释放资源
            jedis.close();
        }
    
  5. 实现 Redis 的整合 -- 把 Redis 功能整合到项目中 -- 使用 Spring框架实现把 Redis 整合到项目中(池子对象在 IOC 容器管理)

    1)最终就是实现使用 IOC 容器管理连接池对象即可

    初始化配置 PoolConfig 参数 最大连接对象、初始化对象;超时时间等等

     <bean class="redis.clients.jedis.JedisPool">
            <!-- 池的属性 -->
            <constructor-arg name="poolConfig" ref="poolConfig"/>
            <!-- Redis主机地址 -->
            <constructor-arg name="host" value="192.168.159.128"></constructor-arg>
            <!-- Redis主机端口 -->
            <constructor-arg name="port" value="6379"/>
            <!-- Redis密码 -->
            <constructor-arg name="password" value="123"/>
            <!-- 可以选择Redis数据库(要选择数据库,必须开启了密码) -->
            <constructor-arg name="database" value="0"/>
            <!-- 链接超时时间 -->
            <constructor-arg name="timeout" value="30000"/>
        </bean>
    
        <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
            <!-- 最大连接数 -->
            <property name="maxTotal" value="10"/>
            <!-- 空闲连接数 -->
            <property name="maxIdle" value="2"/>
            <!-- 设置链接池的连接耗尽时,是否等待 -->
            <property name="blockWhenExhausted" value="true"/>
            <!-- 最大等待时间 -->
            <property name="maxWaitMillis" value="30000"/>
            <!-- 获取到连接时,是否检查链接的有效性 -->
            <property name="testOnBorrow" value="true"/>
        </bean>
    
    @RunWith(SpringRunner.class)
    @ContextConfiguration(locations = "classpath:applicationContext.xml")
    public class RedisTest {
    
        @Autowired
        private JedisPool pool;
    
        @Test
        public void test1() {
            Jedis jedis = pool.getResource();
            String ping = jedis.ping();
            System.out.println(ping);
            jedis.close();
        }
    }
    

    Redis 加密码

    vi redis.conf 搜索并增加 requirepass 123

    本机登录 ./redis-cli -a 密码

    Java连接

    jedis.auth("密码");

  6. 缓存异常

    1)缓存穿透

    模拟多个请求、一直请求不存在的数据,给数据库压力太大(增加判断,防止恶意信息,布隆过滤器、增加空值的key)

    2)缓存击穿

    多线程访问同一个数据,请求缓存存在的数据,多个线程访问,但是数据在缓存有效时间到期了同时穿过缓存向数据库发送多条SQL(热点数据,设置缓存永久有效,防止数据库同时访问量大,增加互斥锁)

    3)缓存雪崩

    同一时间缓存数据大面积失效(让不同缓存失效实现错开,增加互斥锁)

  7. 总结 -- 重点

    1)基本数据结构/数据类型

    2)常用命令

    3)缓存异常


标题:Redis的学习
作者:HB2Ocean
地址:http://future-hb.top:8080/articles/2021/07/26/1627312970091.html