【尚硅谷Redis6】自用学习笔记
Redis介绍
Redis是单线程 + 多路IO复用技术(类似黄牛买票)
默认有16个库,用select进行切换
默认端口号为6379
Memcached:多线程 + 锁(数据类型单一,不支持持久化)
五大常用数据类型
Redis key
- 查看所有键
key *
- 判断key是否存在(返回0/1)
exist key
- 查看key的类型
type key
- 删除key
del key
- 根据value选择非阻塞删除
unlink key
- 设置key的过期时间
expire key 秒数
- 查看key还有多少秒过期,-1表示永不过期,-2表示已经过期
ttl key
- 其他命令
select 切换数据库
dbsize 查看当前数据库key的数量
flushdb 清空当前库
flushall 通杀全部库
String
是二进制安全的,即可以包含任何数据,一个字符串value最多可以是512M,是简单动态字符串,类似ArrayList。
set <key> <value> [EX | PX] [NX | XX]
*NX: key不存在时添加(存在时不覆盖原值)
*XX: key存在时可以添加
*EX: key的超时秒数
*PX: key的超时毫秒数
get <key>
append <key> <value> 追加到原value末尾
strlen <key> 获取值长度
setnx <key> <value> 只有在key不存在时设置
incr <key> 对数字值+1, 只能对数字操作,若为空则设置值为1
decr <key> 对数字值-1,只能对数字操作,若为空则设置值为-1
incrby/decrby <key> <步长> 数字值增减,自定义步长
mset <key1> <value1> <key2> <value2>... 同时设置一个或多个键值对
mget <key1> <key2> <key3>... 同时获取一个或多个value
msetnx <key1> <value1> <key2> <value2>... 同时设置,当且仅当所有key都不存在
getreange <key> <起始位置> <结束位置> 获得值的范围,前包后包
setrange <key> <起始位置> <value> 将从起始位置开始的所有值置为value(下标从0开始)
setex <key> <过期时间> <value> 设置键值的同时,设置过期时间(秒)
getset <key> <value> 设置新值同时获得旧值
List
单键多值,底层是双向链表,可以在两端添加元素(有序),对两端的操作效率较高。
数据结构是quickList,元素较少时用一块连续的内存存储,即ziplist;当数据多的时候才改成quickList(目的是节省空间)。
lpush/rpush <key> <value1> <value2> ... 从左/右插入一个或多个值
lpop/rpop <key> 从左/右弹出一个值。注意:值在键在,值光键亡。
rpoplpush <key1><key2> 从key1右弹一个值,放入key2左边
lrange <key> <start> <stop> 按照下标获得元素(从左到右)
lindex <key> <index> 按下标取元素(从左到右)
llen <key> 获取长度
linsert <key> before/after <value> <newvalue> 在<value>的前/后插入<newvalue>
lrem <key> <n> <value> 从左边删n个value(从左到右)
lset <key> <index> <value> 将列表key下标为index的值替换成value
Set
自动排重,无序。底层是hash表,增删查是O(1)。
数据结构是dict字典。
sadd <key> <value1> <value2> ... 添加元素,已存在则忽略
smembers <key> 取出所有值
sismember <key> <value> 判断是否存在,返回0/1
scard <key> 返回该集合的元素个数
srem <key> <value1> <value2> ... 删除元素
spop <key> 随机弹出集合中的一个值
srandmember <key> <n> 随机得到集合中的n个值,不会被删除
smove <source> <destination> <value> 把集合中一个值从一个集合移动到另一个 集合
sinter <key1> <key2> 返回交集
sunion <key1> <key2> 返回并集
sdiff <key1> <key2> 返回差集(在key1但不在key2)
Hash
键值对集合,是一个string类型的field和value的映射表,特别适合存储对象。类似Map<String, Object>。
长度短且个数较少用ziplist,多了用hashtable
hset <key> <field> <value> 赋值
hget <key> <field> 取值
hmset <key> <field> <value1> <value2> <value3> 批量设置
hexists <key> <field> 是否存在
hkeys <key> 获得所有field
hvals <key> 获得所有value
hincrby <key> <field> <increment>
hsetnx <key> <field> <value>
Zset (sorted set)
没有重复元素的字符串集合(有序),每个成员关联了一个score,集合成员唯一,但score可以重复。
类似Map<String, Double>和TreeSet,底层使用两个数据结构:
- hash,关联value和score
- 跳表,给value排序,根据score范围获取元素列表
zadd <key> <score1> <value1> <score2> <value2> ... 添加
zrange <key> <start> <stop> [WITHSCORES] 返回下标在start和stop之间的元素(可以让分数和值一起返回到结果集)
zrangebyscore key minmax [withscores] [limit offset count] 返回score值在min和max之间的成员(包括两端),按score递增排列
zrevrangebyscore key maxmin [withscores] [limit offset count],同上,从大到小排列
zincrby <key> <increment> <value> 为元素的score加上增量
zrem <key> <value> 删除该集合指定值的元素
zcount <key> <min> <max> 统计分数区间内元素个数
zrank <key> <value> 返回该值在集合中的排名,从0开始
配置文件 redis.conf
Units单位
配置大小单位,开头定义了一些基本的度量单位只支持bytes,不支持bit,大小写不敏感。
Include
类似jsp中的include,多实例的情况可以把公用的配置提取出来。
网络相关配置
bind
- 默认bind = 127.0.0.1 只能接受本机的访问请求。
- 不写的话,无限制接受任何ip地址的方位。
- 生产环境就写应用服务器的地址;服务器是需要远程访问的,所以要注释掉(来支持远程访问)。
- 如果开启了protected-mode,那么在没有设定bind- ip且没有设密码的情况下,Redis只允许接受本机的响应。
port
端口号,默认6379
tcp-backlog
- backlog是一个连接队列,backlog队列总和 = 未完成三次握手队列 + 已完成三次握手队列。
- 在高并发环境下,需要一个高backlog值来避免慢客户端连接问题。
- 注意:Linux内核回把这个值缩小到 /proc/sys/net/core/somaxconn的值(128),所以要增大该值和 /proc/sys/net/ipv4/tcp_max_syn_backlog(128)来达到想要的效果。
timeout
默认是0
tcp-keepalive
心跳检测
通用
daemonize
是否为后台进程,设置为yes,守护进程,后台启动。
pidfile
存放pid文件的位置,每个实例会产生一个不同的pid文件。
loglevel
默认notice
logfile
默认输出日志文件路径
databases
默认16,数据库数量
LIMITS限制
maxclients
最大客户端连接数,默认为10000,达到限制后会拒绝新的连接请求,并回应“max number of clients reached”
maxmemory
建议必须设置,否则内存占满造成服务器宕机。
一旦达到内存使用上线,Redis会试图移除内部数据,一处规则可以通过maxmemory-policy指定。
发布和订阅
pub/sub,是一种消息通信模式,pub发送消息,sub接收消息。Redis客户端可以订阅任意数量的频道。
注意:发布的消息没有持久化,只能收到订阅后发布的消息。
SUBSCRIBE <channel> 订阅
publish <channel> <information> 给channel发布消息(返回订阅者数量)
新数据类型
Bitmaps
其实就是字符串,但只有0和1,可以对字符串的位进行操作。可以想象为一个只有0和1的数组,下标叫偏移量(从0开始)。用来存储活跃用户,会比set节约很大的空间。
setbit <key> <offset> <value> 设置
getbit <key> <offset> 取值
bitcount <key> [start end] 统计1的数量
bitop and/or/not/xor <festkey> [key...] 多个bitmaps的交、并、非、异或,并将结果存在destkey中。
HyperLogLog
是用来做基数统计的算法,优点是:在输入元素的数量或体积非常非常大的时候,计算基数需要的空间总是固定的,并且很小。
pfadd <key> <element> [element ... ] 添加指定元素到HyperLogLog中
pfcount <key> [key .. ] 计算HLL的近似基数
pfmerge <destkey> <sourcekey> [sourcekey ... ] 将一个或多个HLL合并后存在destkey中(如每月活跃用户可以用每天活跃用户来合并计算得到)
Geospatial
用来对地理信息进行操作。
geoadd <key> <经度> <维度> <member> 添加位置
geopos <key> <member> 得到位置信息
geodist <key> <member1> <member2> [m | km | ft | mi] 获得两个位置的直线距离
georadius <key> <经度> <维度> radius m | km | ft | mi 给定经纬度为中心,找出某半径内的元素
Jedis操作
- 在Maven工程中,引入Jedis依赖
- 写一个测试Demo
注意:要禁用Linux防火墙,并在Redis.conf里注释掉bind,然后protected-mode no。
public static void main(String[] args){// ip地址和端口号Jedis jedis = new Jedis("192.168.44.168", 6379);String value = jedis.ping();System.out.println(vaLue);
}
key相关
jedis.set("k1", "v1");
jedis.set("k2", "v2");
jedis.set("k3", "v3");
Set<String> keys = jedis.keys("*");
System.out.println(keys.size());
System.out.println(jedis.exists("k1"));
System.out.println(jedis.ttl("k1"));
System.out.println(jedis.get("k1"));
String相关
jedis.mset("str1", "v1", "str2", "v2", "str3", "v3");
List<String> mget = jedis.mget("str1", "str2", "str3");
list相关
List<String<> list = jedis.lrange("mylist", 0, -1);
set相关
jedis.sadd("orders", "order1");
jedis.sadd("orders", "order2");
jedis.sadd("orders", "order3");
Set<String> smembers = jedis.smembers("orders");
jedis.srem("orders", "order1");
hash相关
jedis.hset("hash1", "username", "lisi");
Map<String, String> map = new HashMap<>();
map.put("tel", "13800009999");
map.put("address", "atguigu");
map.put("email", "abc@163.com");
jedis.hmset("hash2", map);
List<String> result = jedis.hmget("hash2", "tel", "email");
Zset相关
jedis.zadd("zset1", 100d, "z3");
jedis.zadd("zset1", 90d, "l4");
jedis.zadd("zset1", 80d, "w5");
实例:手机验证码
public class PhoneCode{public static void main(String[] args){// 测试逻辑懒得写了,主要是方法}// 生成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;}// 每个手机每天只能发送三次,验证码放到redis中,设置过期时间public static void verifyCode(String phone){Jedis jedis = new Jedis("192.168.44.168", 6379);String countKey = "VerifyCode" + phone + ":count";String codeKey = "VerifyCode" + phone + ":code";String count = jedis.get(countKey);if (count == null){jedis.setex(countKey, 24*60*60, "1");} else if(Integer.parseInt(count) <= 2){jedis.incr(countKey);} else if (Integer.parseInt(count) > 3){System.out.println("今天发送次数已经超过三次");jedis.close();return ;}String vcode = getCode();jedis.setex(codeKey, 120, vcode);jedis.close();}// 验证码校验public static void getRedisCode(String phone, String code){Jedis jedis = new Jedis("192.168.44.168", 6379);String codeKey = "VerifyCode" + phone + ":code";String redisCode = jedis.get(codeKey);if (redisCode.equals(code){System.out.println("成功");} else{System.out.println("失败");}jedis.close ();}
}
Sprint Boot 整合 Redis
- 建立Spring Boot工程,引入相关依赖
- application.properties 配置 redis
- 添加redis配置类 (固定的,现查一个复制粘贴就行)
Redis事务、锁机制、秒杀案例
Redis事务定义
Multi, Exec, discard
事务的错误处理
组队中某个命令报错,执行时整个队列都会被取消;执行时某个命令报错,只有报错的不执行,其他都执行,不会回滚。
事务冲突的问题
悲观锁
操作前上锁,解锁后其他人才能操作。安全,但效率比较低。
乐观锁
加入版本号,所有人都可以得到数据,但操作之后要同步更新版本号,check-and-set机制。适合于多读的应用类型,提高吞吐量。
watch key [key … ]
在执行multi之前,先执行watch,监视一个或多个key,如果在事务执行之前key被其他命令改动,则事务被打断。
使用unwatch可以解除监视。
Redis事务三特性

秒杀案例
public static boolean do SecKill(String uid, String prodid) throws IOException{if (uid == null || prodid == null){return false;}Jedis jedis = new Jedis("192.168.44.168", 6379);String kcKey = "sk:" + prodid + ":qt";String userKey = "sk" + prodid + ":user";// 乐观锁jedis.watch(kcKey);String kc = jedis.get(kcKey);if (kc == null){System.out.println("秒杀还没开始,请等待");jedis.close();return false;}if (jedis.sismember(userKey, uid)){System.out.println("已经秒杀成功,不能重复秒杀");jedis.close();return false;}if (Integer.parseInt(kc) <= 0){System.out.println("秒杀已经结束了");jedis.close();return false;}Transaction multi = jedis.multi();multi.decr(kcKey);multi.sadd(userKey, uid);List<Object> results = multi.exec();if (results == null || results.size() == 0){System.out.println("秒杀失败了");jedis.close();return false;}// jedis.decr(kcKey);// jedis.sadd(userKey, uid);System.out.println("秒杀成功了");jedis.close();return true;
}
并发时存在的问题
- 库存为负(超卖问题)——增加乐观锁
- 可能出现连接超时问题——使用连接池解决(直接粘就行)
- 库存遗留问题(乐观锁造成的)——使用LUA脚本
LUA脚本的优势:
连接池 + lua脚本
Redis 持久化
RDB (Redis DataBase)
是什么
在指定时间间隔内将内存中的数据库快照写入磁盘,即snapshot快照,它恢复时时将快照文件直接读到内存里。
执行过程(保证数据完整性)
Fork
一些配置
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ./
save 时间 改变条数 (手动)
bgsave 是自动的
优势
- 适合大数据的数据恢复
- 对数据完整性和一致性要求不高,更适合使用
- 节省磁盘空间
- 恢复速度快
劣势
- Fork导致内存要2倍
- 写时复制如果数据量大,其实还是很耗性能的
- Redis意外挂了,最后一次持久化后的数据可能丢失
AOF (Append Of File)
- 写命令append到AOF缓冲区
- 根据AOF持久化策略将缓冲区内容同步到磁盘AOF文件中
- AOF文件大小超过重写策略或手动重写时,会rewrite压缩
- Redis重启时根据AOF文件恢复数据
是什么
AOF默认不开启
appendonly yes
AOF和RDB同时开启,默认读取AOF的数据(数据不会存在丢失)
AOF异常恢复
AOF同步频率设置
appendfsync always 始终同步,每次写入都记录,性能差但数据完整性好
appendfsync everysec 每秒记录一次,如果宕机,本秒数据可能丢失
appendfsync no 不主动同步,把同步时机交给操作系统
Rewrite压缩
set a a1
set b b1
会被压缩为:
set a a1 b b1
重写时和RDB是类似的,即写时复制技术。
优势
- 备份机制更稳健,丢失数据概率低
- 可读的日志文本,通过操作AOF文件,可以处理误操作
劣势
- 比RDB占用更多磁盘空间
- 恢复备份速度慢
- 每次读写都同步的话,有一定性能压力
- 存在个别bug造成恢复不能
用哪个好?
主从复制
是什么
主机数据更新后,根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主。
能干嘛
- 读写分离,性能扩展
- 容灾快速恢复
搭建主从复制
- 创建/myredis文件夹
- 复制redis.conf到文件夹中
- 配置一主两从,创建三个配置文件
- redis6379.conf
- redis6380.conf
- redis6381.conf
- 写入内容
- 使用命令启动三个服务
redis-server redis6379.conf
- 查看三台主机运行情况
info replication
- 配从不配主
slaveof <ip> <port> 成为某个实例的从服务器
一主两仆
从服务器挂了,重启后变成普通服务器,不再是从服务器,需要再次配置猜能变成从服务器;
主服务器挂了,重启后仍然是主服务器。
复制原理(有全量和增量)
- 当从连上主服务器之后,从服务器向主服务器发送进行数据同步消息(从服务器主动);
- 主服务器接到消息后,进行数据持久化得到rdb文件,把rdb文件发送给从服务器,从服务器拿到rdb文件后进行读取;
- 每次主服务器进行写操作之后,和从服务器进行数据同步(主服务器主动);
薪火相传
从服务器向下边的从服务器传数据,缺点是上边的从服务器挂了,下边就收不到数据了。
反客为主
一个master宕机后,后边的slave可以立刻升为master,其后边的slave不用做任何修改。
缺点:手动完成,没法自动完成。
slaveof no one 从机变主机
哨兵模式(sentinel)
是什么
反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从转换为主。
怎么玩
- 调整为一主二仆,6379带着6380、6381
- 自定义的 /myredis 目录下新建sentinel.conf文件,名字绝对不能错
- 配置哨兵,填写内容
sentinel monitor mymaster 127.0.0.1 6379 1
mymaster为监控对象起的服务器名称,1为至少有多少个哨兵同意迁移的数量
- 启动哨兵
redis-sentinel /myredis/sentinel.conf
- 主机挂掉,从机选举产生新主机
根据 slave-priority 选择从机,原主机重启后会变成从机。
选择条件依次为:- 选择优先级靠前的(在redis.conf里默认:replica-priority 100,值越小优先级越高)
- 选择偏移量最大的(原主机数据最全的)
- 选择runid最小的(每个redis实例启动后都会随机生成一个40位的runid)
复制延时
所有写操作都是在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。
注:Jedis里有相关配置代码,上网找一个粘就行。
集群
问题
容量不够如何扩容?——用集群
并发写操作,如何分摊?——用集群
主从模式、薪火相传模式,主机挂了导致ip地址发生变化,应用程序的配置需要修改对应的主机地址、端口等信息。——无中心化集群配置(任何一台服务器都可以作为集群的入口,所有服务器互相连通状态)
特点
- Redis集群实现了对Redis的水平扩容,即启动 N 个Redis节点,将整个数据库分布存储在这 N 个节点中,每个节点存储总数居的 1 / N;
- Redis集群通过分区(partition)来提供一定程度的可用性(availability),即使集群中有一部分节点失效或者无法通讯,集群也可以继续处理命令请求。
删除持久化数据
rdb 和 aof 文件都删掉就行
搭建集群玩玩
- 制作6个实例:6379、6380、6381、6389、6390、6391
- 配置基本信息
开启 daemonize yes
Pid 文件名字
指定端口
Log 文件名字
Dump.rdb 名字
Appendonly 关掉或换名字
- redis cluster 配置修改
cluster-enabled yes 打开集群模式
cluster-config-file nodes-6379.conf 设定节点配置文件名
cluster-node-timeout 15000 设定节点失联时间,超过该时间(毫秒),集群自动进入主从切换
- 启动6个redis服务
- 将6个节点合成一个集群
注意:需要确保所有redis实例启动后,nodes-xxxx.conf文件都正常生成。
cd /opt/redis-6.2.1/src 进入redis安装目录
redis-cli --cluster create --cluster-replicas 1 192.168.11.101:6379 192.168.11.101:6380 192.168.11.101:6381 192.168.11.101:6389 192.168.11.101:6390 192.168.11.101:6391
–replicas 1 采用最简单的方式配置集群,一台主机一台从机,刚好三组
注意:这里要使用真实ip地址,不要用127.0.0.1
- -c 采用集群策略连接,设置数据会自动切换到相应的写主机
redis-cli -c -p 6379
- 通过命令查看集群信息
cluster nodes
- redis cluster如何分配这6个节点?
一个集群至少要有三个主节点。
分配原则尽量保证每个主数据库运行在不同的IP地址,每个从库和主库不在一个IP地址上。
什么是slots(插槽)
就是哈希槽,数据库中的每个键都属于这些插槽中的一个,集群使用CRC16(key) % 插槽数量来计算key属于哪个槽(计算CRC16校验和)。集群中的每个节点负责处理一部分插槽。
**注意:**不在一个slot的键值,不能使用 mset 和 mget 等多键操作!
解决方法:通过{}定义组,从而使key中{}内相同内容的键值对放到一个slot中去。
mset k1{cust} v1 k2{cust} v2
查询集群中的值
注意:只能看自己的插槽啊,看不到别人的
cluster keyslot cust
cluster countkeysinslot xxxx
cluster getkeysinslot xxxx
故障恢复
如果主节点挂了,从节点能否自动升为主节点?注意:15s超时
主节点恢复后,主从关系会如何?主节点回来变成从机。
如果拥有某一段插槽的主从节点都挂了,redis服务还能继续吗?
- 如果 cluster-require-full-converage 为yes,则整个集群挂掉;
- 为no,该插槽数据全部不能使用,也无法存储;
注:该参数在redis.conf中
集群的Jedis开发
public class JedisClusterTest{public static void main(String[] args){HostAndPort hostAndPort = new HostAndPort("192.168.31.211", 6379);JedisCluster jedisCluster = new JedisCluster(hostAndPort);jedisCluster.set("k1", "v1");System.out.println(jedisCluster.get("k1"));jedisCluster.close();}
}
优点
实现扩容、分摊压力、无中心配置相对简单
不足
- 多键操作不支持
- 多键的Redis事务不被支持,lua脚本不被支持
- 出现时间较晚,已经采用其他集群方案的公司想要迁移到redis cluster,需要整体迁移而不是逐步过渡,复杂度较高
应用问题解决
缓存穿透
观察到的现象:
- 应用服务器压力变大了
- redis命中率降低
- 一直查询数据库,然后数据库崩溃了
发生什么事
- redis查询不到数据库
- 出现很多非正常url访问
原因
遭到攻击
解决方案
- 对空值缓存:如果一个查询返回的数据为空(无论数据是否存在),我们扔把空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过5min
- 设置可访问的名单(白名单):用 bitmaps 类型定义一个可以访问的名单,名单 id 作为 bitmaps 的偏移量,每次访问和 bitmaps 里的 id 进行比较,如果访问 id 不在 bitmaps 里,进行拦截,不允许访问
- 采用布隆过滤器(Bloom Filter):1970年由布隆提出的。实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。布隆过滤器可以用于检索一个元素是否存在一个集合中。它的优点是空间效率和查询时间都远远超过一般算法,缺点是有一定的误识别率和删除困难
- 进行实时监控:当发现 Redis 的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务
缓存击穿
现象
- 数据库访问压力瞬时增加
- redis 里面没有出现大量的 key 过期
- redis 正常运行
原因
- redis 某个 key 过期了,大量访问使用这个 key
解决方案
- 预先设置热门数据:在 redis 高峰访问之前,把一些热门数据提前存入到 redis里面,加大这些热门数据 key 的时长
- 实时调整:现场监控哪些数据热门,实时调整 key 的过期时长
- 使用锁:
1)在缓存失效的时候(判断拿出来的值为空),不是立即去 load db
2)先使用缓存工具的某些带成功返回值的操作(如 Redis 的 SETNX)去 set 一个 mutex key
3)当操作返回成功时,再进行 load db操作,并回设缓存,最后删除 mutex key
4)当操作返回失败,证明有线程在 load db,当前线程睡眠一段时间再重试整个 get 缓存的方法
缓存雪崩
现象
- 数据库压力变大
- 服务器崩溃
原因
- 在极少时间内,查询大量 key 的集中过期情况
解决方法
- 构建多级缓存架构:nginx 缓存 + redis 缓存 + 其他缓存(ehcache等)
- 使用锁或队列:用加锁或队列的方式保证不会有大量线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况
- 设置过期标志更新缓存:记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际 key 的缓存
- 将缓存失效时间分散开:比如我们可以在原有的失效时间上加一个随机值,比如 1-5 min 随机,这样每一个缓存过期时间的重复率就会降低,很难引发集体失效的事件
分布式锁
单纯 Java API 不能提供分布式锁的能力,需要一种跨 JVM 的互斥机制来控制共享资源的访问。
分布式锁主流的实现方案:
- 基于数据库实现分布式锁
- 基于缓存(Redis等)
- 基于Zookeeper
性能上 redis 最高,可靠性上 zookeeper 最高。
使用redis实现分布式锁
- setnx + del 实现上锁和释放锁
- 锁一直没有释放,设置 key 过期时间,自动释放
- 上锁后突然出现异常,无法设置过期时间——上锁时同时设置过期时间
set users 10 nx ex 12
UUID防止误删
@GetMapping("testLock")
public void testLock(){Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111", 3, TimeUnit.SECONDS);if (lock){Object value = redisTemplate.opsForValue().get("num");if (StringUtils.isEmpty(value)){return ;}int num = Integer.parseInt(value + " ");redisTemplate.opsForValue().set("num", ++num);redisTemplate.delete("lock");} else{try{Thread.sleep(100);testLock();} catch (InterruptedException){e.printStackTrace();}}
}
问题:可能释放其他服务器的锁
@GetMapping("testLock")
public void testLock(){String uuid = UUID.randomUUID().toString();Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);if (lock){Object value = redisTemplate.opsForValue().get("num");if (StringUtils.isEmpty(value)){return ;}int num = Integer.parseInt(value + " ");redisTemplate.opsForValue().set("num", ++num);String lockUuid = (String) redisTemplate.opsForValue().get("lock");if (lockUuid.equals(uuid)) {redisTemplate.delete("lock");}} else{try{Thread.sleep(100);testLock();} catch (InterruptedException){e.printStackTrace();}}
}
LUA保证删除原子性
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEY[1]) else return 0 end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(script);
redisScript.setResultType(Long.class);
redisTemplate.execute(redisScript, Arrays.asList(lockKey), uuid);
确保分布式锁满足以下4个条件:
- 互斥。任意时刻,只有一个客户端能持有锁;
- 不会发生死锁。即使有一个客户端在持有锁期间挂了而没有主动解锁,也能保证后续其他金额护短能加锁;
- 加锁和解锁必须是同一个客户端,并且不能把别人加的锁释放;
- 加锁和解锁必须具有原子性。
相关文章:
【尚硅谷Redis6】自用学习笔记
Redis介绍 Redis是单线程 多路IO复用技术(类似黄牛买票) 默认有16个库,用select进行切换 默认端口号为6379 Memcached:多线程 锁(数据类型单一,不支持持久化) 五大常用数据类型 Redis key …...
产品更新丨谷云科技ETLCloud V3.9.2版本发布
谷云科技 ETLCloud 集成平台迎来了每月一次的功能迭代,本月发布版本号为 3.9.2 版本,为用户带来了新的功能、优化改进以及问题修复,以下是详细介绍: 新增组件 本次更新新增了众多实用组件,涵盖了京东和 Shopify 相关…...
Promise并发控制与HTTP请求优化
Promise并发方法对比 #mermaid-svg-tnmGzOkgNUCrbvfI {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-tnmGzOkgNUCrbvfI .error-icon{fill:#552222;}#mermaid-svg-tnmGzOkgNUCrbvfI .error-text{fill:#552222;stroke…...
G1垃圾回收器中YoungGC和MixedGC的区别
在 G1 垃圾回收器中,Mixed GC 和 Young GC 的区别主要体现在以下几个方面: 作用范围 Young GC:仅针对年轻代中的Region进行回收,包括 Eden 区和 Survivor 区的 Region。Mixed GC:会回收所有年轻代的 Region 以及部分…...
Web4.0身份革命:去中心化身份系统的全栈实现路径
去中心化身份(DID)技术栈正在重构数字世界的信任根基,本文从密码学协议、存储网络、验证框架三个维度,解析符合W3C标准的身份系统构建方案。通过Hyperledger Aries架构实践,揭示如何实现跨链身份互通、数据主权控制、零…...
iOS/Flutter混合开发之PlatformView配置与使用
环境:Xcode 16.3、Flutter 3.29.2、Dart 3.7.2。 使用背景:需要在flutter界面中嵌入一个iOS原生控件。 步骤: 1. iOS侧实现: 1.1:PlatformView实现 class FLNativeView: NSObject, FlutterPlatformView {private v…...
Libconfig 修改配置文件里的某个节点
THCommandStatus ( { Status "1"; index 5; }, { Status "2"; index 8; }, { Status "3"; index 7; }, { Status "4"; index 0; } ); 比如这是配置文件的内容ÿ…...
【金仓数据库征文】_AI 赋能数据库运维:金仓KES的智能化未来
AI 赋能数据库运维:金仓KES的智能化未来 🌟嗨,我是LucianaiB! 🌍 总有人间一两风,填我十万八千梦。 🚀 路漫漫其修远兮,吾将上下而求索。 在当今数字经济飞速发展的时代࿰…...
【MySQL】3分钟解决MySQL深度分页问题
什么是深度分页问题?该如何解决呢?这篇文章展开讲讲 什么是深度分页? 当查询结果集非常大时,需要获取靠后页码的数据,比如第1000页、10000页。 如: SELECT * FROM table LIMIT 10000, 10; -- 获取第10001-10010条…...
GitHub 趋势日报 (2025年04月24日)
本日报由 TrendForge 系统生成 https://trendforge.devlive.org/ 📈 今日整体趋势 Top 10 排名项目名称项目描述今日获星总星数语言1kortix-ai/sunaSuna - Open Source Generalist AI Agent⭐ 1105⭐ 3639TypeScript2cloudcommunity/Free-CertificationsA curated …...
一种双模式机器人辅助股骨干骨折钢板植入方法
股骨干骨折是一种常见的高能损伤,微创内固定是首选治疗方法。然而,钢板植入过程中存在不可见、不准确和不稳定等问题。山东大学研究团队提出了一种双模式机器人辅助钢板植入方法,通过神经网络模型规划钢板植入轨迹,然后利用机械臂…...
全球碳化硅晶片市场深度解析:技术迭代、产业重构与未来赛道争夺战(2025-2031)
一、行业全景:从“材料突破”到“能源革命”的核心引擎 碳化硅(SiC)作为第三代半导体材料的代表,凭借其宽禁带(3.26eV)、高临界击穿场强(3MV/cm)、高热导率(4.9W/cmK&…...
IDEA搭建环境的五种方式
一、普通的java项目 File--New--Project 选择Java,jdk选择1.8版本,然后点next 输入项目名和路径名,点击Finish 创建包结构,编写Class类 编写主方法,输出Hello标志完成 二、普通的javaWeb项目 Java Enterprise-- 勾选…...
隐形革命:环境智能如何重构“人-机-境“共生新秩序
引言 在万物互联的时代,环境智能(Ambient Intelligence, AmI)正以“隐形革命者”的姿态重塑人类生活场景。通过分布式传感器、边缘计算与自适应算法的深度融合,AmI构建出能感知、学习并响应人类行为的智慧环境。 本文基于多领域研…...
Mysql唯一性约束
唯一性约束(Unique Constraint)是数据库设计中用于保证表中某一列或多列组合的值具有唯一性的一种规则。它可以防止在指定列中插入重复的数据,有助于维护数据的完整性和准确性。下面从几个方面为你详细解释 作用 确保数据准确性:…...
QuecPython+GNSS:实现快速定位
概述 QuecPython 结合 GNSS(全球导航卫星系统)模块为物联网设备提供开箱即用的定位能力解决方案。该方案支持 GPS/北斗/GLONASS/Galileo 多系统联合定位,为物联网开发者提供从硬件接入到云端服务的全栈式定位解决方案。 优势特点 多体系定…...
【从零开始:自制一个Java消息队列(MQ)】
🚀 从零开始:自制一个Java消息队列(MQ) 在现代分布式系统中,消息队列(Message Queue,MQ)已经成为一个至关重要的组件。它帮助系统在异步处理、负载均衡、解耦等方面提供了强大的支持…...
WHAT - 已阅读书单
指数基金投资指南✅ 我们终将变富 纳瓦尔宝典 围城✅ 许三观卖血记✅ 骆驼祥子✅ 活着 白鹿原✅ 百年孤独 君主论 阿Q正传✅ 蛤蟆先生去看心理医生✅ 思考,快与慢 三体✅ 人类简史:从动物到上帝✅ 明朝那些事✅ 三国演义✅ 中国历代政治得失✅ 资治…...
代码随想录算法训练营第60期第十七天打卡
今天我们继续进入二叉树的下一个章节,今天的内容我在写今天的博客前大致看了一下部分题目难度不算大,那我们就进入今天的题目。 第一题对应力扣编号为654的题目最大二叉树 这道题目的坑相当多,我第一次题目没有看明白就是我不知道到底是如何…...
C 语言数组详解
一、数组的基本概念 在 C 语言中,数组是一种相同数据类型元素的集合,这些元素在内存中连续存储。通过数组,我们可以用一个统一的名字来管理一组相关的数据,并且通过下标(索引)快速访问其中的每一个元素。例…...
ADVB协议同步
关于视频传输,有多种控制时序。协议标准允许设计者选择有限的几个速率的接口来满足 系统设计目标。例如,一些系统使用总线时序发送信息通过line-by-line;在这个案例中, 容器的sof作为vsync同步的点。horizontal line blanding将插入idles,ADV…...
基于LAB颜色空间的增强型颜色迁移算法
本文算法使用Grok完成所有内容,包含算法改进和代码编写,可大大提升代码编写速度,算法改进速度,提供相关idea,提升效率; 概述 本文档描述了一种基于LAB颜色空间的颜色迁移算法,用于将缩略图D的…...
复合材料高置信度 DIC 测量与高级实验技术研讨会邀请函
邀请函 2025年5月8日 上海 中国复合材料学会官网会议通知 您可以点击上方蓝字跳转官网查看官网信息 主办单位: 中国复合材料学会 协办单位: 研索仪器科技(上海)有限公司 数字图像相关技术(Digital Image Correla…...
解决docker部署MySQL的max_allowed_packet 限制问题
报错 Error querying database. Cause: com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large (2,471 > 2,048). You can change this value on the server by setting the max_allowed_packet variable. ### The error may exist in file …...
【XR手柄交互】Unity 中使用 InputActions 实现手柄控制详解(基于 OpenXR + Unity新输入系统(Input Actions))
摘要: 本文主要介绍如何使用 Input Actions(Unity 新输入系统) OpenXR 来实现 VR手柄控制(监听ABXY按钮、摇杆、抓握等操作)。 🎮 Unity 中使用 InputActions 实现手柄控制详解(基于 OpenXR 新…...
C++:继承机制详解
目录 一.继承的概念及定义 一).继承的概念 二).继承定义 1.定义格式 2.继承类型 3.继承类模板 二.基类和派生类间的转换 三.继承中的作用域 四.派生类的默认成员函数 一).4个常见默认成员函数 二).不可被继承的类 五…...
自然语言处理+知识图谱:智能导诊的“大脑”是如何工作的?
智能导诊系统定义与作用 智能导诊系统是一种基于人工智能技术的医疗辅助工具,旨在提高医疗服务效率、改善患者就医体验、降低医院运营成本,通过自然语言处理技术,智能导诊系统能够自动回答患者的常见问题,帮助患者快速了解自己的…...
论文阅读笔记——ZeroGrasp: Zero-Shot Shape Reconstruction Enabled Robotic Grasping
ZeroGrasp 论文 多视角重建计算大、配置复杂,本文将稀疏体素重建(快且效果好)引入机器人抓取且只考虑单目重建,通过利用基于物理的接触约束与碰撞检测(这对精确抓取至关重要),提升三维重建质量将…...
Qt 调试信息重定向到本地文件
1、在Qt软件开发过程中,我们经常使用qDebug()输出一些调试信息在QtCreator终端上。 但若将软件编译、生成、打包为一个完整的可运行的程序并安装在系统中后,系统中没有QtCreator和编译环境,那应用程序出现问题,如何输出信息排查…...
Android Studio开发中Application和Activity生命周期详解
文章目录 Application生命周期Application生命周期概述Application关键回调方法onCreate()onConfigurationChanged()onLowMemory()onTrimMemory()onTerminate() Application生命周期管理最佳实践 Activity生命周期Activity生命周期概述Activity生命周期回调方法onCreate()onSta…...
vite+vue2+elementui构建之 package.json
webpack版本太低,构建依赖太多,头大。 各种查阅资料,弄了一份直通构建vite构建elementUi核心文件, 构建基于开源若依vue2vue3版本改造,感谢开源,感谢若依。 vitevue2elementui构建之 vite.config.js-CSD…...
安全编排自动化与响应(SOAR):从事件响应到智能编排的技术实践
安全编排自动化与响应(SOAR):从事件响应到智能编排的技术实践 在网络安全威胁复杂度指数级增长的今天,人工处理安全事件的效率已难以应对高频攻击(如日均万级的恶意IP扫描)。安全编排自动化与响应…...
《Keras 3 :使用 TFServing 提供 TensorFlow 模型》
《Keras 3 :使用 TFServing 提供 TensorFlow 模型》 作者:Dimitre Oliveira 创建日期:2023/01/02 最后修改时间:2023/01/02 描述:如何使用 TensorFlow Serving 为 TensorFlow 模型提供服务。 (i) 此示例使用 Keras 3 在 Colab 中查看 GitHub 源 介绍 构建机器学习模…...
深入理解C++ 中的list容器
目录 一、引言 二、list的基本介绍 2.1 底层结构 2.2 特性 三、list的使用 3.1 构造函数 3.2 迭代器的使用 3.3 容量相关操作 3.4 元素访问相关操作 3.5 修改器操作 3.6 迭代器失效问题 四、list的模拟实现 4.1 节点定义 4.2 迭代器实现 4.3 list类实现 五、lis…...
附赠二张图,阐述我对大模型的生态发展、技术架构认识。
文章精炼,用两张图说明大模型发展业态方向,以及大模型主体技术架构。(目前还需要进一步验证我的Thought && ideas,等待机会吧.........) 图一:探究大模型三个层次应用方向,浅层次入门简…...
Java实现加密(七)国密SM2算法的签名和验签(附商用密码检测相关国家标准/国密标准下载)
目录 一、国密标准中,关于SM2签名验签的定义二、SM2签名和验签的实现原理1. 前置知识2. 签名生成过程3. 验签过程4. 数学正确性证明5. 安全性与注意事项 三、带userId、不带userId的区别1. 核心区别2.算法区别(1) 哈希计算过程(2) 签名验签流程 四、Java代码实现1. …...
贪心算法~~
目录 一、理论基础 二、题目练习 (1)455. 分发饼干 (2)53. 最大子数组和 - 力扣 (3)122. 买卖股票的最佳时机 II - 力扣(LeetCode) (4)860. 柠檬水找零…...
XYNU2024信安杯-REVERSE(复现)
前言 记录记录 1.Can_you_find_me? 签到题,秒了 2.ea_re 快速定位 int __cdecl main_0(int argc, const char **argv, const char **envp) {int v4; // [esp0h] [ebp-1A0h]const char **v5; // [esp4h] [ebp-19Ch]const char **v6; // [esp8h] [ebp-198h]char v7;…...
NLP系列【自然语言处理的深度学习模型综述】
自然语言处理的深度学习模型 摘要传统自然语言处理模型(略 不作重点)神经网络自然语言处理模型经典神经网络CNN网络模型Word2Vec模型RNN模型GPT网络模型BERT网络模型 BERT变体模型提升模型性能模型压缩 摘要 在自然语言处理任务方面,依据语料…...
【差分隐私】basic primitive的含义
在差分隐私领域,“basic primitive”一词具有特定的技术含义,需从单词本义及学科背景两个层面解析: 一、单词本义解析 “Primitive”在计算机科学中通常指代基础构建单元或核心组件,例如编程语言中的基本数据类型(如整…...
数字浪潮下的算力担当:GPU 服务器的多元应用、核心价值
在当今数据洪流和信息爆炸的时代,算力已成为衡量国家、行业乃至企业发展水平的关键指标。而算力服务器,特别是 GPU 服务器,作为算力的核心载体,正以其卓越性能深刻改变着世界的运行逻辑与模式。从数据处理到云计算,从人…...
【Echarts】使用echarts绘制多个不同类型的中国地图
一、需求 在同一页面上绘制多个不同类型的中国地图,如果是在同一页面上绘制多个同一种类型的地图可以直接引用一个china.js文件,设置两个独立的div分别用于放置两个地图,并实例化配置相关参数即可,但是如果在同一个页面上绘制多个…...
WEB漏洞-XSS跨站原理分类
本文主要内容 原理 XSS漏洞产生原理? XSS漏洞危害影响? 分类 反射型、存储型、DOM型 手法 XSS平台使用 XSS工具使用 XSS结合其他漏洞 靶场搭建 pikachu 靶场搭建(完整版)-CSDN博客https://blog.csdn.net…...
PR第二课--混剪
1.音乐打点 1.1 手动打点 按钮(如图),或者,快捷键M(如果在已有打点处,再次按M键会进入对标记点的设置界面,如下下图) 1.2 插件打点 一段音乐中,有明显的鼓点时,可以使用打点插件,快捷打点;如果鼓点不明显的话,最好还是手动打点,用插件打点会打出大量的标记点,…...
Kafka和flume整合
需求1:利用flume监控某目录中新生成的文件,将监控到的变更数据发送给kafka,kafka将收到的数据打印到控制台: 在flume/conf下添加.conf文件, vi flume-kafka.conf # 定义 Agent 组件 a1.sourcesr1 a1.sinksk1 a1.c…...
Linux 内核网络协议栈中 inet_stream_ops 与 tcp_prot 的深度解析
在 Linux 内核网络协议栈中,TCP 协议的实现依赖于多个关键结构体的协作。其中,inet_stream_ops 和 tcp_prot 是两个核心结构体,它们分别属于不同的层次,共同完成从用户态系统调用到底层协议处理的完整链路。本文将从功能定位、协作关系、代码示例及设计哲学等方面,深入分析…...
flume整合kafka
需求一: 启动flume 启动kafka消费者,验证数据写入成功 新增测试数据 需求二: 启动Kafka生产者 启动Flume 在生产者中写入数据...
EasyRTC音视频实时通话嵌入式SDK,打造社交娱乐低延迟实时互动的新体验
一、方案背景 在数字化时代,社交娱乐已经成为人们生活中不可或缺的一部分。随着移动互联网和智能设备的普及,用户对实时互动的需求越来越高。EasyRTC作为一款基于WebRTC技术的实时音视频通信解决方案,凭借其低延迟、高稳定性和跨平台兼容性&…...
制作一款打飞机游戏21:自定义工具
关于如何在Pico 8中创建我们自己的编辑器。 外部编辑器的需求 首先,我想谈谈为什么我们需要外部编辑器。外部编辑器通常用于编辑游戏中的数据。例如,一个游戏卡或程序通常包含一些代码,但也会包含数据,比如静态信息,…...
面向高性能运动控制的MCU:架构创新、算法优化与应用分析
摘要:现代工业自动化、汽车电子以及商业航天等领域对运动控制MCU的性能要求不断提升。本文以国科安芯的MCU芯片AS32A601为例,从架构创新、算法优化到实际应用案例,全方位展示其在高性能运动控制领域的优势与潜力。该MCU以32位RISC-V指令集为基…...