HyperLogLog(Redis新数据类型)
简介
为解决基数问题(求集合中不重复元素个数的问题),又不占用非常大的空间。
(数据存储在mysql表中使用distinct count计算不重复个数,或使用Redis提供的hash、set、bitmaps等数据结构来处理,以上的方案结果精确,但随着数据不断增加,导致占用空间越来越大)
redis提出一种降低一定精度来平衡存储空间的HyperLogLog,redis HyperLogLog是用来做基数统计的算法,HyperLogLog的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的,并且是很小的
zairedis里面,每个HyperLogLog键只需要花费12kb内存,就可以计算接近2^64个不同元素的基数,这和计算基数时,元素越多越耗费内存的集合形成鲜明对比
但是,因为HyperLogLog只会根据输入元素来计算基数,而不会储存输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素
常用命令
pfadd [element...]添加指定元素到HyperLogLog
pfvount[key...]计算HLL的近似基数,可以计算多个HLL。
pfmerge[sourcekey...] 将一个或多个HLL合并后的结果存储在另一个HLL中
Geospatial(Redis新数据类型)
简介
Redis 3.2 中增加了对GEO类型的支持。GEO,Geographic,地理信息的缩写。该类型,就是元素的2维坐标,在地图上就是经纬度。redis基于该类型,提供了经纬度设置,查询,范围查询,距离查询,经纬度Hash等常见操作。
常用命令
geoadd [longitude latitude member...]添加地理位置(经度,纬度,名称)
两级无法直接添加,有效的经度从-180度到180度有效的维度从-85.05112878度到85.05112878度
已经添加的数据,是无法再次往里面添加的
geopos [member...]获取坐标值
geodist [m|km|ft|mi]获取两个位置之间的直线距离(米”默认值“,千米,英里,英尺)
georadius radius m|km|ft|mi 以给定的经纬度为中心,找出某一半径内的元素
Redis Jedis测试
Java 使用 Redis | 菜鸟教程
https://www.runoob.com/redis/redis-java.html
<!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>4.2.3</version> </dependency>
包括了安装,连接,使用实例等。(连接本地时记得改改config文件里面有个属性是控制本地连接的。)
(还有防火墙这一层,需要把6379端口放行,以防链接超时)
完成一个手机验证码功能
要求:
1、输入手机号,点击发送后随机生成6位数字码,2分钟有效
2、输入验证码,点击验证,返回成功或失败
3、每个手机号每天只能输入3次
package com.deng.util; import redis.clients.jedis.Jedis; import java.util.Random; /** * @Author shayu * @DateTime 2022/8/23 14:17 */ public class PhoneCode { public static void main(String[] args) { //模拟验证码发送 verifyCode("123456789101"); //校验验证码是否相同 getRedisCode("123456789101","234311"); } //3.验证码的校验 public static void getRedisCode(String phone,String code){ //从redis中获取验证码 Jedis jedis = new Jedis("localhost",6379); //验证码key String codeKey = "VerifyCode" + phone + ":code"; String redisCode = jedis.get(codeKey); //判断 if (redisCode.equals(code)){ System.out.println("成功"); }else { System.out.println("失败"); } jedis.close(); } //2.每个手机每天只能发送三次,验证码放到redis中,设置过期时间 public static void verifyCode(String phone){ //连接本地的 Redis 服务 Jedis jedis = new Jedis("localhost",6379); // 如果 Redis 服务设置了密码,需要下面这行,没有就不需要 // jedis.auth("123456"); System.out.println("连接成功"); //查看服务是否运行 System.out.println("服务正在运行: "+jedis.ping()); //拼接key //手机发送key的次数 String countKey = "VerifyCode" + phone + ":count"; //验证码key String codeKey = "VerifyCode" + phone + ":code"; //每个手机每天发送三次 String count = jedis.get(countKey); if (count == null){ //没有发送过,第一次发送 //设置发送次数为1 jedis.setex(countKey,24*60*60,"1"); }else if (Integer.parseInt(count) <= 2){ //发送次数+1 jedis.incr(countKey); }else if (Integer.parseInt(count) > 2){ System.out.println("今天的发送次数超过三次"); jedis.close(); return; } //将生成的验证码放到redis里面去 String vcode = getCode(); jedis.setex(codeKey,120,vcode); jedis.close(); } //1.生成6位数字的验证码 public static String getCode(){ Random random = new Random(); String code = ""; for (int i = 0; i < 6; i++) { int rand = random.nextInt(10); code += rand ; } return code; } } /** * 有一个小问题,方法过期,也就是会不会有人卡着过期时间去要验证码, * 是不是就可以一直要了。(但过期时间有是一天,不会有人这么无聊吧。 * 可以延伸一下,具体这么做我也不太清楚。) * 这也只是一个小荔枝,具体实际业务会更加复杂。 * */
Springboot整合Redis
SpringBoot之Redis访问(spring-boot-starter-data-redis) - 码农教程
http://www.manongjc.com/detail/25-fidimhvkzoxchrg.html
(我偷懒不想写具体教程,需要的可以看看跟着写写)
Redis 事务操作
定义
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务的主要作用就是串联多个命令防止别的命令插队。
Multi、Exec、discard
从输入Multi命令开始(事务开始),输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec(执行)后,Redis会将之前的命令队列中的命令依次执行。
组队的过程中可以通过discard来放弃组队。
组队成功,提交成功
组队阶段报错,提交失败,组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消。
组队成功,提交有成功有失败情况,如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
事务冲突
想想一个场景:有很多人有你的银行卡,同时去参加双十一抢购
一个请求想给金额减8000
一个请求想给金额减5000
一个请求想给金额减1000
最终会导致账号金额出现负数,是不合理的。(禁止贷款)
解决以上问题
悲观锁
悲观锁,顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到他拿到锁。在操作之前先上锁。
乐观锁
乐观锁,顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。redis就是利用这种check-and-set机制实现事务的。
watch key[key...]
在执行multi之前,先执行watch key1[key2]。可以监视一个或多个key,如果在事务执行之前这个key被其他命令所改动,那么事务将被打断。
set balance 100,执行watch就行监视,操作balance+10,可以完成。
同时开启监视,开启事务,balance+20,但前面已经进行了对key的监视,发生了变动,事务执行就会打断。
Redis事务的三大特性
- 单独的隔离操作
- 事务中的所有命令都会序列化,按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
- 没有隔离级别的概念
- 队列中的命令没有提交之前都不会实际被执行,因为事务提交之前任何指令都不会被实际执行
- 不保证原子性
- 事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。
Redis持久化
为什么要有持久化?(当我看到持久化时,我第一反应就是为什么要持久化,然后去搜了一下,这位博主讲的特别有意思,简单来说就是怕redis突然宕机数据丢失做的一种保护化措施。)
redis提供了两个不同形式的持久化方式
- RDB(Redis DataBase)
- AOF(Append Of File)
RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘(硬盘),也就是行话讲的Snapshot快照,他恢复时是将快照文件直接读到内存里
是redis默认持久化的方式
执行
redis会单独创建一个子进程(fork)来进行持久化,会先将数据写入到一个临时文件中,待持久化过程结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。 如果需要进行大规模的数据恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加高效。RDB的缺点是最后一次持久化后的数据可能丢失。(这句我当时也不太清楚,继续往下看。)
fork延伸
(说实话我也没弄太明白这是啥,浅显理解是个进程吧)> - Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
- 在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术”
- 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。