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操作]的主要内容,如果未能解决你的问题,请参考以下文章
Redis学习笔记3:五大数据类型(StringListSetHashZset)和三种特殊数据类型(geospatialHyperloglogBitmaps)