Redis学习笔记 [配置文件,3种新的数据类型,Jedis操作]

Posted 小智RE0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis学习笔记 [配置文件,3种新的数据类型,Jedis操作]相关的知识,希望对你有一定的参考价值。

近期计划对redis再进行一段详细的学习,
在B站找到了尚硅谷的redis教学视频->【尚硅谷】Redis 6 入门到精通 超详细 教程,进行学习记录

文章目录


🛴1.配置文件redis.conf


首先看到的这一部分; 表示配置大小的单位,定义了基本的度量单位,仅支持bytes,不支持bit;大小写都支持.


第二部分:includes部分;相当于之前学习JSP时,使用的include的包含功能;
可以将其他问价引用到个配置文件中;
将一些公共的配置部分就放置在这里;


紧接着看到的这个配置;代表当前配置下的redis仅支持本地连接


配置protected-mode 可配置是否开启保护模式; 这里配置为no之后,即可支持远程访问;

端口号port默认为 6379


可看到配置tcp-backlog;默认为511

  • 这里的backlog实际就是一个链接队列
  • 队列总和为 : 未完成三次握手的队列 + 已经完成三次握手队列;
  • 在高并发的环境下需要设置一个高 backlog值避免客户端的慢连接问题;
  • 注意linux内核会把这个值减小到 /proc/sys/net/core/somaxconn的值 (128),那么就需要确认增加/proc/sys/net/core/somaxconn/proc/syc/net/ipv4/tcp_max_syn_backlog(128)两个值来达到效果


参数timeout 默认为0

表示配置一个空闲的客户端将会维持多少秒会关闭,
默认为0表示该功能默认不使用,即 永远不会关闭客户端;

参数tcp-keepalive 默认为300
表示每隔300秒就会检测一次该tcp连接是否存活;


参数:daemonize 可配置是否可以后台启动服务;
改为yes后即可支持后台启动服务.


即参数pidfile可配置存放pid日志文件;每个实例都会生成一个不同的pid文件;
可看到这里的redis_6379.pid即日志文件;


参数loglevel表示配置的日志级别;
默认为notice级别:即 生产环境;

  • 支持的四种日志级别;(1):debug:开发测试级别;(2)verbose:列斯与debug级别,但是目录比较清楚;
    (3)warning:记录警告消息;(4) notice:适用于生产环境的日志级别

参数logfile可设置日志的输出保存位置


databases 参数表示数据库的个数


配置项:Clients客户端配置
参数 maxclients;

  • 可设置redis在同一时刻可以和多少客户端进行连接; 默认是10000个;
  • 当达到该限制时,redis就会拒绝掉新的请求,且对连接请求响应max number of clients reached

参数maxmeory ;可设置redis使用的内存量,一旦达到内存的使用上限,redis可尝试移出内部数据,
这个移出的具体策略可以通过maxmemory-policy来配置;

若不进行设置,有可能会将内存占满.

关于移除的策略 参数 maxmemory-policy说明;

  • volatile-lru (仅对设置了过期时间的键.)使用LRU算法(最近最少使用算法)移除key;
  • allkeys-lru 对集合的所有key,使用LRU算法进行移除;
  • volatile-random 在过期集合中随机移除(仅对设置过期时间的key)key.
  • volatile-ttl 移除ttl值最小(即将过期的key)的key,
  • allkeys-random 移除随机的key;
  • noevicition 仅针对写操作,返回错误信息,而不是移除处理

参数maxmemory-samples; 可设置样本大小,redis默认会检测多个key且选择其中LRU的;

一般设置3~7即可,数值样本越小性能消耗越小,但是样本越不准确;


🛴2.Redis的发布与订阅


🌈2.1基本概念


redis的发布与订阅,可以看做是一种通信关系;客户端可以订阅任意数量的频道.

  • 发布者(pub)发出消息,
  • 订阅者(sub)接收消息

模拟客户端订阅频道


当发布者向频道发布消息后,就发给订阅了频道的客户端.


🌈2.2模拟实现


首先打开两个连接,开启两个redis服务;

打开一个客户端订阅channel1;

输入命令 subscribe channel1

打开另一个客户端,为channel1发布消息 helloxiaozhi;返回1 即订阅者数量;
publish channel1 helloxiaozhi

这里在客户端1即可收到消息


🛴3.Redis6的新数据类型


🌈3.1.新数据类型(1)—bitmaps


📢3.1.1基本概念及常用命令


如今的计算机采用了二进制/位 作为信息基础单位,1个字节等于8位,
例:字符串"abc"由3个字节组成,但在实际计算机存储时采用二进制表示,
"abc"对应的ascll码为 97,98,99. 对应二进制为 01100001, 01100010, 01100011.

  • 使用操作位可有效提升内存使用率;
  • bitmaps本身并不能称之为数据类型,实际就是字符串的键值对形式,但是bitmaps可对字符串的位进行操作.
  • 可将bitmaps 看作是以位作为单位的数组,数组的每个单元仅可存储0/1,数组的下标在bitmaps就是偏移量.


bitmaps与set比较
假设网站目前有1亿用户,每天可以独立访问的用户具有5千万,若每天用集合类型与bitmaps分别存储活跃用户的信息时,可以创建这样一张表:

在存储独立用户信息方面,bitmaps的效率也是不错的.

但是当独立用户过少时,需要记录每天的访问用户,那么bitmaps的效率可能会有所下降.


常用命令

  • setbit key offset value 命令可设置 bitmaps中某个偏移量的值, (注意:偏移量从零开始).

  • getbit key offset可获取到 bitmaps中的某个偏移量的值.(由0开始)

  • bitcount key start end 可统计字符串中从 开始->结束 字节范围中,比特值为1的数量;

    • 这个一般情况下,给定的整个字符串都会进行计数操作,通过额外的start/end参数,可以让计数仅在特定的位上进行,start与end参数的设置,都可以使用负数值,

    • 例如-1可表示最后一个位,-2表示倒数第2个位,

  • bitop and(or/not/xor) destkey key...... 作为复合操作,可以做多个bitmaps的(and)交集,(or)并集,xor(异或),(not)非, 操作结果存入到destkey中.


📢3.1.2练习操作


为练习setbit命令,模拟一个场景;

  • 假设某网站会将 每个独立用户是否访问过网站存放在bitmaps中,将访问的用户计为1,没有访问的用户计为0,用偏移量作为用户的id.
  • 在设置键的第 offset个位的值(由0开始),假设有20个用户, 目前id为 1,6,11,15,19的用户对网站进行了访问,那么bitmaps的初始化效果为:

首先进入redis

模拟纪录 1,6,11,15,19 的用户访问

那么现在可以用getbit 命令查看某个用户是否来访问过网站;返回0则说明未访问过;


可使用 bitcount 统计当前网站的用户访问数量.

统计用户的id在第1个字节 到 第 4个字节的人数


练习 bitop操作

  • 首先存入 2022年4月1日访问网站的用户记录;
    id为1,2,3,6,10,15,23的用户

  • 然后存入2022年4月2日访问网站的用户记录
    id为0,1,6,12,23,25的用户.

这里返回4是因为去除了相同的数字


🌈3.2.新数据类型(2)—HyperLogLog


📢3.2.1基本概念及常用命令


在实际处理数据时,都会涉及到统计的需求,可采用redis中的 incr incrby命令进行实现,
但是对于一些独立的访客统计/独立IP数,搜索记录数等需要去重/计数问题;

  • 可以将数据存储到mysql数据库中,使用 distinct count 去重处理查询;
  • 可使用redis的hash/set.bitmaps 类型去重;

那么随着数据量的增加,在效率方面是否会有问题;
此时,redis就提供了一种新的数据类型 : HyperLogLog

  • HyperLogLog 可以用来做基数统计, 计算基数所需要的空间总是固定的,且数据空间小.

  • 在redis中,每个HyperLogLog的键仅需要消耗 12KB内存,即可计算接近于 264 个不同元素的基数;

  • 由于HyperLogLog仅根据输入元素来计算基数,而不是存储输入元素本身,所以它并不能像别的集合那样子返回输入的元素.

基数:

比如说一个数据集1,3,5,7,5,7,8 ,那么该数据集的基数集为1,3,5,7,8;去重处理.
基数估计: 在可接受的误差范围内,快速计算基数.


基本命令

  • pfadd key element element... 可添加指定的元素到HyperLogLog中,若成功则返回1,若失败则返回0;

  • pfcount key [key....] 可计算 HLL的近似基数,可计算多个HLL,

    • 比如用HLL存储了每一天的UV,计算一周的UV[(unique visitor )即独立访客数]时:即可用7天的合并计算.

  • pfmerge destkey sourcekey [sourcekey ...] 可以将一个/多个HLL合并后的结果存入到另一个HLL中

    • 比如每月的活跃用户可以使用每天的活跃用户合并计算得到,


📢3.2.2练习操作


练习使用 pfadd命令添加元素到redis数据库中.

可用pfcount 命令统计数量

可使用命令pfmerge合并两个/多个集合的计数


🌈3.3.新数据类型(3)—Geospatial


📢3.3.1基本概念及常用命令


  • GEO:即地理信息. 这个数据类型就是元素的2位坐标,即地图上得经纬度;
  • 基于该数据类型可以进行经纬度设置,查询,范围查询,距离查询,经纬度哈希操作.

常用命令

  • geoadd key (longitude)经度 (latitude)纬度 地址名称 存储具体的经纬度信息,(注意数据的合理范围)

    • 注意两极坐标无法直接添加;已经添加的数据,无法再次存入,

    • 有效的经度范围: -180°~180° ,有效的纬度范围从 -85.05112878°到85.05112878°.

  • geopos key member member.... 可以获取指定地区的坐标值

  • geodist key 地址1 地址2 m/km/mi/ft 可根据坐标计算到两个地址直线距离,

    • 可设置具体的长度单位:[m:米,km:千米,ft:英尺,mi:英里 ];默认使用米作为单位

  • georadius key longitude latitude radius m|km|mi|ft 以给定的经纬度为中心,可找到某一半径内的元素.


📢3.3.2练习操作


可以用geoadd 命令设置存储几个地址的经纬度信息.

可用geopos命令 查询到指定地址的经纬度信息.


可用命令geodist 计算两个地址之间的直线距离.


🛴4.Jedis操作学习


🌈4.1Jedis操作测试


首先直接创建一个maven项目;

pom.xml文件中导入jedis依赖

<dependencies>
    <!--jedis-->
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.2.0</version>
    </dependency>
    <!--测试使用-->
    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.12</version>
       <scope>compile</scope>
   </dependency>
</dependencies>

直接写个类测试

public class Demo1 
    public static void main(String[] args) 
        //创建Jedis对象; 放入ip和端口.
        Jedis jedis = new Jedis("192.168.59.128",6379);
        //输入redis密码; (无密码可跳过此步骤)
        jedis.auth("我的redis密码");
        //测试;
        String value = jedis.ping();
        System.out.println(value);
    

运行,测试成功;

注意,若连接失败,请查看redis的配置文件 redis.conf中是否开放了远程访问, 是否关闭了保护模式.

还有一种情况是,可能防火墙拦截了;

  • 先用命令查看防火墙状态 systemctl status firewalld ;
  • 然后再使用命令关闭防火墙:systemctl stop firewalld

//测试方法1;
    @Test
    public void demoMethod() 
        //创建Jedis对象; 放入ip和端口.
        Jedis jedis = new Jedis("192.168.59.128", 6379);
        //输入redis密码; (无密码可跳过此步骤)
        jedis.auth("你的redis密码");
        //测试;
        Set<String> keys = jedis.keys("*");
        for (String s : keys) 
            System.out.println(s);
        
    


单个存数据,取数据测试


//测试方法2;
 @Test
 public void demoMethod2() 
     //创建Jedis对象; 放入ip和端口.
     Jedis jedis = new Jedis("192.168.59.128", 6379);
     //输入redis密码; (无密码可跳过此步骤)
     jedis.auth("你的redis密码");
     //测试存储数据;
     jedis.set("name","小智");
     jedis.set("age","22");

     //获取数据;
     System.out.println(jedis.get("name"));
     System.out.println(jedis.get("age"));
 


多个键存数据,取数据测试


//测试方法3;
@Test
public void demoMethod3() 
    //创建Jedis对象; 放入ip和端口.
    Jedis jedis = new Jedis("192.168.59.128", 6379);
    //输入redis密码; (无密码可跳过此步骤)
    jedis.auth("你的redis密码");
    //测试存储数据;
    jedis.mset("k1","val1","k2","val2","k3","val3");
    //获取数据;
    System.out.println(jedis.mget("k1","k2","k3"));


操作list类型的数据


//测试方法4;
@Test
public void demoMethod4() 
    //创建Jedis对象; 放入ip和端口.
    Jedis jedis = new Jedis("192.168.59.128", 6379);
    //输入redis密码; (无密码可跳过此步骤)
    jedis.auth("你的redis密码");
    //测试存储数据; 依次从左边添加数据;
    jedis.lpush("list1","xiaojie","xiaoma","xiaol","xiaozhi");
    //获取list1列表的所有数据;
    List<String> vals = jedis.lrange("list1", 0, -1);
    System.out.println(vals);


操作set类型的数据


//测试方法5;
@Test
public void demoMethod5() 
    //创建Jedis对象; 放入ip和端口.
    Jedis jedis = new Jedis("192.168.59.128", 6379);
    //输入redis密码; (无密码可跳过此步骤)
    jedis.auth("你的redis密码");
    //测试存储数据;
    jedis.sadd("sets","xiaoma");
    jedis.sadd("sets","xiaoli");
    jedis.sadd("sets","xiaoming");
    jedis.sadd("sets","xiaojie");
    //获取sets集合的所有数据;
    Set<String> vals = jedis.smembers("sets");
    System.out.println(vals);


操作hash类型的数据


 //测试方法6;
@Test
public void demoMethod6() 
    //创建Jedis对象; 放入ip和端口.
    Jedis jedis = new Jedis("192.168.59.128", 6379);
    //输入redis密码; (无密码可跳过此步骤)
    jedis.auth("你的redis密码");
    //测试存储数据;
    //存单个数据;
    jedis.hset("hash1", "name", "xiaozhi");
    System.out.println(jedis.hget("hash1", "name"));

    //存储一个map;
    Map<String, String> map = new HashMap<>();
    map.put("phone", "13626111111");
    map.put("address", "斜塔小镇");
    map.put("email", "fortnite@fn.com");
    jedis.hmset("hash2", map);
    List<String> list = jedis.hmget("hash2", "phone", "address");
    for (String s : list) 
        System.out.println(s);
    


操作zset类型的数据


//测试方法7;
@Test
public void demoMethod7() 
    //创建Jedis对象; 放入ip和端口.
    Jedis jedis = new Jedis("192.168.59.128", 6379);
    //输入redis密码; (无密码可跳过此步骤)
    jedis.auth("你的redis密码");
    //测试存储数据;
    jedis.zadd("zset01", 100d, "zhangsan");
    jedis.zadd("zset01", 60d, "lisi");
    jedis.zadd("zset01", 200d, "mark");
    jedis.zadd("zset01", 600d, "keli");
    jedis.zadd("zset01", 10d, "morks");
    //获取数据;
    Set<String> zset01 = jedis.zrange("zset01", 0, -1);
    for (String s : zset01) 
        System.out.println(s);
    


🌈4.2Jedis—模拟验证码发送


模拟场景:

  • 输入手机号,点击发送后随机生成6位数字码,2分钟内有效;
  • 输入验证码之后,点击验证,返回成功/失败;
  • 每个手机号一天内仅可以输入3次.

那么在具体实现时,可利用redis的过期失效功能,存储验证码;
限制手机获取验证码次数可用redis中的 incr递增操作,每次获取验证码时,加1次标记,达到3次则禁止获取验证码.


package com.xiaozhi.jedis_study;

import redis.clients.jedis.Jedis;

import java.util.Random;

/**
 * @BelongsProject: jedis_demostudy
 * @BelongsPackage: com.xiaozhi.jedis_study
 * @Author: 信计1801 李智青
 * @Date: 2022/4/2 18:15
 * @Description: 模拟手机验证码功能;
 */
public class SimulateMobilePhoneVerificationCode 
    public static void main(String[] args) 
        //先发送验证码
        //cacheVerificationCode("66666666666");
        //验证使用;
        System.out.println(verifyTheCodeIsTrue("66666666666", "552150"));
    

    /**
     * 判断比较验证码
     *
     * @param phone       手机号
     * @param VersionCode 码
     */
    public static String verifyTheCodeIsTrue(String phone, String VersionCode) 
        //创建Jedis对象; 放入ip和端口.
        Jedis jedis = new Jedis("192.168.59.128", 6379);
        //输入redis密码; (无密码可跳过此步骤)
        jedis.auth("你的redis密码");

        String keyForCode = "independent" + phone + "code";
        //从redis中获取到验证码;
        String codeFromRedis = jedis.get(keyForCode);
        jedis.close();
        if (VersionCode.equals(codeFromRedis)) 
            return "验证码正确";
         else 
            return "验证码错误/已失效";
        
    

    /**
     * 缓存验证码,且限制一个手机在一天内仅可以获取3次验证码;
     *
     * @param phone 手机号
     */
    public static void cacheVerificationCode(String phone) 
        //创建Jedis对象; 放入ip和端口.
        Jedis jedis = new Jedis("192.168.59.128", 6379);
        //输入redis密码; (无密码可跳过此步骤)
        jedis.auth("你的redis密码")以上是关于Redis学习笔记 [配置文件,3种新的数据类型,Jedis操作]的主要内容,如果未能解决你的问题,请参考以下文章

Redis6.x学习笔记复制

Redis学习笔记09Redis数据类型之 哈希表类型

Redis学习笔记

Redis 学习笔记总结

Redis 学习笔记Redis配置文件

Redis学习笔记3:五大数据类型(StringListSetHashZset)和三种特殊数据类型(geospatialHyperloglogBitmaps)