Redis --- 分布式锁的使用
我们在上篇博客高并发处理 --- 超卖问题+一人一单解决方案讲述了两种锁解决业务的使用方法,但是这样不能让锁跨JVM也就是跨进程去使用,只能适用在单体项目中如下图:
为了解决这种场景,我们就需要用一个锁监视器对全部集群进行监视:

这就引出了分布式锁的概念。
什么是分布式锁?
分布式锁是一种在分布式系统中用于控制多个实例(如多个微服务节点)对共享资源的并发访问的机制。分布式锁的核心目标是避免在并发情况下出现数据不一致的问题,比如多个线程同时对同一数据进行操作时,可能会导致数据竞争、脏数据或者业务逻辑错误。
总而言之:分布式锁就是满足分布式系统或者集群模式下多进程可见并且互斥的锁。
为什么要使用分布式锁?
在单机环境中,使用常规的同步机制(如 Java 中的
synchronized)可以避免并发问题,但在分布式系统中,多个服务或应用实例可能同时操作共享的资源。传统的同步方法无法跨机器或进程工作,因此需要引入分布式锁来确保在多台机器中,只有一个节点可以操作共享资源,其他节点必须等待。
分布式锁的核心:实现多进程之间互斥。
分布式锁的实现方式有很多种,主要使用分布式系统中能共享的工具和技术。常见的实现方式包括基于 数据库、Redis、Zookeeper 等的分布式锁。

那么接下来我们就对基于Redis实现分布式锁进行讲解:
Redis 是目前使用最广泛的分布式锁实现方式之一,因为 Redis 提供了高性能的存储和锁机制,适合高并发的场景。

我们在SimpleRedisLock类去继承ILock接口,以此来实现获取释放锁的过程:
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;public class SimpleRedisLock implements ILock{private static final String KEY_PREFIX = "lock:";private String keyName;private StringRedisTemplate stringRedisTemplate;public SimpleRedisLock(String keyName, StringRedisTemplate stringRedisTemplate) {this.keyName = keyName;this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean tryLock(long timeoutSec) {// 获取当前线程标识long threadId = Thread.currentThread().getId();// 获取锁Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + keyName, threadId + "", timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}@Overridepublic void unlock() {// 释放锁stringRedisTemplate.delete(KEY_PREFIX + keyName);}
}
随后我们通过自定义的锁工具类进行使用:
首先new一个工具类对象,通过对象调用获取锁方法,随后判断是否获得锁,之后使用动态代理获得代理对象后,使用代理对象调用事务方法。
业务内容请点击下面地址:高并发处理 --- 超卖问题+一人一单解决方案
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Autowiredprivate ISeckillVoucherService seckillVoucherService;@Autowiredprivate RedisIdWorker redisIdWorker;@Resourceprivate StringRedisTemplate stringRedisTemplate;public Result seckillVoucherBySelf(Long voucherId) {// 业务逻辑...Long userId = UserHolder.getUser().getId();
// synchronized (userId.toString().intern()){
// IVoucherOrderService proxy = (IVoucherOrderService)AopContext.currentProxy(); // 获取当前类的代理对象 (需要引入aspectjweaver依赖,并且在实现类加入@EnableAspectJAutoProxy(exposeProxy = true)以此来暴露代理对象)
// return proxy.createVoucherOrder(voucherId);
// }SimpleRedisLock lock = new SimpleRedisLock("order" + userId, stringRedisTemplate);boolean isLock = lock.tryLock(1200);// 判断是否获取锁成功if (!isLock) {// 获取锁失败,返回错误或重试return Result.fail("不允许重复下单");}try {IVoucherOrderService proxy = (IVoucherOrderService)AopContext.currentProxy(); // 获取当前类的代理对象 (需要引入aspectjweaver依赖,并且在实现类加入@EnableAspectJAutoProxy(exposeProxy = true)以此来暴露代理对象)return proxy.createVoucherOrder(voucherId);} finally {lock.unlock();}}@Transactionalpublic Result createVoucherOrder(Long voucherId) {// 业务逻辑... }
}
但是这样会出现一种情况:虽然我们的线程1还没有结束,但是由于锁设定的时间到期而被释放销毁,此时线程2就能够开始获取锁,过一段时间后线程1结束就会要释放锁,这个时候释放的锁就是线程2刚加上去的锁,所以导致线程安全问题。

解决方案:在获取锁的过程存入线程标识(可用UUID表示),以便于在释放锁去判断这个锁是不是自己的,如果是则释放,如果不是则不释放。(总而言之:设置线程标识的目的是判断释放锁是不是同一个线程获取的,以此来避免类似线程2创建的锁被线程1释放引发的线程安全问题)

那么我们就需要再释放锁的方法中加入判断逻辑:
public class SimpleRedisLock implements ILock{private static final String KEY_PREFIX = "lock:"; // 锁的前缀private static final String ID_PREFIX = UUID.randomUUID().toString() + "-"; // 线程标识的前缀private String keyName;private StringRedisTemplate stringRedisTemplate;public SimpleRedisLock(String keyName, StringRedisTemplate stringRedisTemplate) {this.keyName = keyName;this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean tryLock(long timeoutSec) {// ...}@Overridepublic void unlock() {// 获取线程标识String threadId = ID_PREFIX + Thread.currentThread().getId();// 获取锁中标识String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + keyName);// 判断标识是否一致if(threadId.equals(id)){// 如果一致则释放锁stringRedisTemplate.delete(KEY_PREFIX + keyName);}}
}
但是还是有问题,请看下图:

如果出现一种情况,在线程1事务结束后释放锁,这时按照我们上述逻辑,会判断锁key与线程标识是否一致,随后假如非常凑巧,这个时候发生了线程阻塞并且锁还正好到期而释放,那么这个时候线程2就会获取锁,随后线程1阻塞结束后就会执行释放锁的代码,那么这个时候刚好就会将线程2创建的锁释放掉。
所以这个时候我们就必须保证判断锁标识与释放锁是原子性的。
这个时候我们就可以使用 lua 脚本编写多条Redis命令,从而确保多条命令执行时的原子性。
可以参考下面网站学习:Lua 教程 | 菜鸟教程
什么是Lua脚本?
LUA 是一种轻量级的脚本语言,具有简单、易于嵌入等特点。在 Redis 中,LUA 脚本用于实现服务器端的逻辑操作,它可以在 Redis 服务器上执行,并且具有 原子性,即 Redis 在执行 LUA 脚本时,会确保脚本中的所有操作都要么全部执行成功,要么全部不执行。
Redis 支持通过
EVAL命令执行 LUA 脚本,使用这种方式可以实现一些复杂的操作,避免了在客户端执行多个 Redis 命令时可能发生的网络延迟和操作不一致问题。
LUA 脚本如何确保原子性?
在 Redis 中,LUA 脚本是原子性执行的,即在脚本执行过程中,其他 Redis 命令不会被插入。这是因为 Redis 会一次性地加载、解析并执行整个脚本,直到脚本执行完毕,Redis 才会处理其他命令。因此,在一个脚本中,所有的操作都在 Redis 服务器端执行,不会中断,也不会被其他命令打断。
这就意味着在执行 LUA 脚本时,Redis 会确保以下几点:
- 脚本中的所有命令要么都执行,要么都不执行:例如,如果一个脚本修改了多个 Redis 键值,并且中途遇到了错误,那么整个脚本的操作将会失败,Redis 会保证在执行过程中没有部分操作成功,部分操作失败的情况。
- 不需要担心并发问题:多个客户端同时执行相同的 LUA 脚本时,它们会按顺序依次执行,不会出现竞争条件。
- 性能提升:将多个操作通过一个 LUA 脚本提交给 Redis 执行,避免了多次网络请求,提高了性能。
LUA 脚本在 Redis 中的使用方式
Redis 通过
EVAL或EVALSHA命令执行 LUA 脚本:
- EVAL 命令:直接执行脚本。
- EVALSHA 命令:执行已加载的脚本(通过
SCRIPT LOAD命令加载脚本),使用脚本的 SHA1 校验和来标识脚本。一个 LUA 脚本的示例:
local current = redis.call('GET', KEYS[1]) if current thenredis.call('SET', KEYS[1], current + 1) elseredis.call('SET', KEYS[1], 1) end return redis.call('GET', KEYS[1])假设我们有一个 Lua 脚本要将一个键的值更新为某个参数:
return redis.call('set', KEYS[1], ARGV[1])在 Redis Lua 脚本中,
KEYS[]和ARGV[]是两个特殊的数组,用来传递键和值。它们的内容由execute方法的keys和args参数提供。
KEYS[]数组用于传递 Redis 中的键,通常在 Lua 脚本中用于表示数据库中需要操作的 Redis 键。你可以在 Lua 脚本中通过KEYS[1]、KEYS[2]等方式来引用这些键。ARGV[]数组用于传递非键值的参数。这些参数通常用于脚本中的计算、条件判断等,而不是 Redis 键。例如,ARGV可以用来传递数字、字符串等作为参数传给 Redis 命令。
那么我们将java代码提取改造成Lua脚本:
// 获取线程标识
String threadId = ID_PREFIX + Thread.currentThread().getId();
// 获取锁中标识
String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + keyName);
// 判断标识是否一致
if(threadId.equals(id)){// 如果一致则释放锁stringRedisTemplate.delete(KEY_PREFIX + keyName);
}
那么改造后的Lua脚本:
-- 锁的key
local key = KEYS[1]
-- 当前线程标识
local threadId = ARGV[1]-- 获取锁中的线程标识 get key
local id = redis.call('get', key)
-- 比较线程标识与锁中的标识是否一致
if(id == threadId) then-- 释放锁 del keyreturn redis.call('del', key)
end
return 0
我们下载EmmyLua插件,在/resource/unlock.lua文件中将上面代码粘贴进入即可。
随后我们在unlock方法内进行修改,按住Ctrl+H在右面即可查看类型的全部继承类。
为了调用Lua脚本文件,我们可以使用 RedisTemplate 的 execute 方法,这样可以直接执行 Lua 脚本,并传递相应的参数。RedisTemplate 的 execute 方法允许你执行自定义的 Redis 命令,包括 Lua 脚本。
在Java底层,execute()方法的源码如下:
public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) {return this.scriptExecutor.execute(script, keys, args); }1.
RedisScript<T> script:RedisScript是一个用于表示 Redis 脚本(通常是 Lua 脚本)的对象。T是脚本执行结果的返回类型。你可以将 Lua 脚本作为RedisScript的参数传入,执行该脚本后,它会返回一个类型为T的结果。2.
List<K> keys:keys参数是 Lua 脚本的KEYS[]数组,对应传入 Redis 的键。这些键是你在 Lua 脚本中使用的KEYS[]部分。Redis 脚本允许你使用多个键(最多可以传递 16 个键),这些键是传递给 Lua 脚本的。3.
Object... args:args是可变参数,表示 Lua 脚本中的ARGV[]数组。在 Lua 脚本中,ARGV[]用于传递参数,这些参数通常是值(字符串、数字等),而不是 Redis 键。args可以是任意类型的参数。
public class SimpleRedisLock implements ILock{private static final String KEY_PREFIX = "lock:"; // 锁的前缀private static final String ID_PREFIX = UUID.randomUUID().toString() + "-"; // 线程标识的前缀private String keyName;private StringRedisTemplate stringRedisTemplate;public SimpleRedisLock(String keyName, StringRedisTemplate stringRedisTemplate) {this.keyName = keyName;this.stringRedisTemplate = stringRedisTemplate;}// Lua代码private static final DefaultRedisScript<Long> UNLOCK_SCRIPT; // 泛型内填入返回值类型static { // 静态属性要使用静态代码块进行初始化UNLOCK_SCRIPT = new DefaultRedisScript<>();UNLOCK_SCRIPT.setResultType(Long.class);UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));}// 调用Lua脚本@Overridepublic void unlock() {// 调用Lua脚本stringRedisTemplate.execute(UNLOCK_SCRIPT,Collections.singletonList(KEY_PREFIX + keyName),ID_PREFIX + Thread.currentThread().getId()); }
}
但是基于我们自己创建的锁有以下几个方面的问题:
- 不可重入:同一个线程无法多次获取同一把锁。
- 不可重试:获取锁只尝试一次就返回false,没有重试机制。
- 超时释放:锁超时释放虽然可以避免死锁,但是如果是业务执行耗时较长,也会导致锁的释放,存在安全隐患问题。
- 主从一致性:如果Redis提供了主从集群,主从同步存在延迟,当主宕机时,如果从并同步主中的锁数据,则会出现锁实现。
那么这个时候我们就需要使用Redission来实现分布式锁:
下面是官方文档: Redission --- 快速入门
Redisson 是一个基于 Redis 的客户端,它提供了分布式锁的功能,适用于解决分布式系统中资源的竞争问题。我们可以通过
Redisson来实现分布式锁,确保同一时刻只有一个实例能处理某个资源或任务。
首先引入依赖:
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.32.0</version>
</dependency>
之后配置Redission的客户端:
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient() {// 配置 Redisson 客户端Config config = new Config();// 添加redis的地址,也可使用config.useClusterServers()添加集群地址config.useSingleServer().setAddress("redis://localhost:6379").setPassword("password"); // 如果有密码return Redisson.create(config);}
}
之后是使用Redission实现的分布式锁的案例:
Redisson 提供了 RLock 对象,允许在业务逻辑中加锁和释放锁。
例如,假设有一个需要保证只允许一个实例执行的业务操作,可以这样做:
@Service
public class BusinessService {@Autowiredprivate RedissonClient redissonClient;public void performBusinessLogic() {// 获取锁,锁名为 "myLock",设置等待时间和锁的持有时间RLock lock = redissonClient.getLock("myLock");try {// 尝试获取锁,最多等待 10 秒,锁的持有时间为 30 秒// tryLock(long waitTime, long leaseTime, TimeUnit unit) if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {try {// 执行需要加锁的业务逻辑System.out.println("Executing business logic...");// 例如处理任务,更新数据库等} finally {// 释放锁lock.unlock();}} else {System.out.println("Could not acquire the lock, try again later.");}} catch (InterruptedException e) {e.printStackTrace();}}
}
那么上面代码使用Redission实现的分布式锁为:
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Autowiredprivate ISeckillVoucherService seckillVoucherService;@Autowiredprivate RedisIdWorker redisIdWorker;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Resourceprivate RedissonClient redissonClient;public Result seckillVoucherBySelf(Long voucherId) {// ...Long userId = UserHolder.getUser().getId();
// SimpleRedisLock lock = new SimpleRedisLock("order" + userId, stringRedisTemplate);
// boolean isLock = lock.tryLock(1200);RLock lock = redissonClient.getLock("lock:order:" + userId);boolean isLock = lock.tryLock();// 判断是否获取锁成功if (!isLock) {// 获取锁失败,返回错误或重试return Result.fail("不允许重复下单");}try {IVoucherOrderService proxy = (IVoucherOrderService)AopContext.currentProxy(); // 获取当前类的代理对象 (需要引入aspectjweaver依赖,并且在实现类加入@EnableAspectJAutoProxy(exposeProxy = true)以此来暴露代理对象)return proxy.createVoucherOrder(voucherId);} finally {lock.unlock();}}
}
下图讲述了Redission实现分布式锁的原理:
那么如何实现可重入锁呢?
我们先了解什么是可重入锁:
Redis 的 可重入锁 是指同一个线程可以多次获得锁,而不会导致死锁。Redisson 提供了内置的可重入锁(RLock)功能,能够自动支持锁的可重入性。它的原理是通过记录每个线程对锁的持有次数来实现的,每当一个线程重新获取锁时就会增加持有次数,释放锁时会检查持有次数,直到持有次数为 0 才会真正释放锁。
我们可以按照下面逻辑进行分析:

当然,我们这里的读写锁的操作同样要保证原子性,那么这个时候就需要写Lua脚本:
(1)判断锁是否存在:①不存在:获取锁,设置有效期 ②存在且是自己的:重入次数+1,设置有效期
local key = KEYS[1]; -- 锁的key
local threadId = ARGV[1]; -- 线程唯一标识
local releaseTime = ARGV[2]; -- 锁的自动释放时间
-- 判断是否存在
if(redis.call('exist',key) == 0) then-- 不存在,获取锁redis.call('hset',key,threadId,'1');-- 设置有效期redis.call('expire',key,releaseTime);
end;
-- 锁已经存在,判断threadId是否是自己
if(redis.call('hexists',key,threadId) == 1) then-- 不存在,获取锁,重入次数+1redis.call('hincrby',key,threadId,1);-- 设置有效期redis.call('expire',key,releaseTime);return 1; -- 返回结果
end;
return 0; -- 代码走到这里,则说明获取锁的不是自己,获取锁失败
(2)判断锁是否是自己的:
- 不是就直接返回 nil
- 是自己锁就 -1 并判断重入次数是否为 0:① >0则重置有效期 ② =0则直接删除锁
local key = KEYS[1]; -- 锁的key
local threadId = ARGV[1]; -- 线程唯一标识
local releaseTime = ARGV[2]; -- 锁的自动释放时间
-- 判断当前锁是否还是被自己持有
if(redis.call('hexists',key,threadId) == 0) thenreturn nil; -- 如果已经不是自己,则直接返回
end;
-- 是自己的锁,则冲入次数-1
local count = redis.call('hincrby',key,threadId,-1);
-- 判断重入次数是否已经=0
if(count > 0) then-- 不为0,说明不能释放锁,则重置有效期然后返回redis.call('expire',key,releaseTime);return nil;
else -- =0说明可以释放锁,直接删除锁redis.call('del',key);return nil;
end;
在使用分布式锁时,尤其是可重入锁,务必注意以下几点:
- 锁的自动释放:设置 releaseTime 时间确保锁在指定时间内被释放,防止线程因为异常或超时没有释放锁而导致死锁。
- 及时释放锁:在业务执行完后,一定要确保调用
unlock()方法释放锁。尤其在复杂业务场景下,确保每个分支都有对应的释放逻辑,避免丢锁。
相关文章:
Redis --- 分布式锁的使用
我们在上篇博客高并发处理 --- 超卖问题一人一单解决方案讲述了两种锁解决业务的使用方法,但是这样不能让锁跨JVM也就是跨进程去使用,只能适用在单体项目中如下图: 为了解决这种场景,我们就需要用一个锁监视器对全部集群进行监视…...
用C++编写一个2048的小游戏
以下是一个简单的2048游戏的实现。这个实现使用了控制台输入和输出,适合在终端或命令行环境中运行。 2048游戏的实现 1.游戏逻辑 2048游戏的核心逻辑包括: • 初始化一个4x4的网格。 • 随机生成2或4。 • 处理玩家的移动操作(上、下、左、…...
【设计模式-行为型】状态模式
一、什么是状态模式 什么是状态模式呢,这里我举一个例子来说明,在自动挡汽车中,挡位的切换是根据驾驶条件(如车速、油门踏板位置、刹车状态等)自动完成的。这种自动切换挡位的过程可以很好地用状态模式来描述。状态模式…...
CentOS/Linux Python 2.7 离线安装 Requests 库解决离线安装问题。
root@mwcollector1 externalscripts]# cat /etc/os-release NAME=“Kylin Linux Advanced Server” VERSION=“V10 (Sword)” ID=“kylin” VERSION_ID=“V10” PRETTY_NAME=“Kylin Linux Advanced Server V10 (Sword)” ANSI_COLOR=“0;31” 这是我系统的版本,由于是公司内网…...
【flutter版本升级】【Nativeshell适配】nativeshell需要做哪些更改
flutter 从3.13.9 升级:3.27.2 nativeshell组合库中的 1、nativeshell_build库替换为github上的最新代码 可以解决两个问题: 一个是arg("--ExtraFrontEndOptions--no-sound-null-safety") 在新版flutter中这个构建参数不支持了导致的build错误…...
使用 Vue 3 的 watchEffect 和 watch 进行响应式监视
Vue 3 的 Composition API 引入了 <script setup> 语法,这是一种更简洁、更直观的方式来编写组件逻辑。结合 watchEffect 和 watch,我们可以轻松地监视响应式数据的变化。本文将介绍如何使用 <script setup> 语法结合 watchEffect 和 watch&…...
使用shell命令安装virtualbox的虚拟机并导出到vagrant的Box
0. 安装virtualbox and vagrant [rootolx79vagrant ~]# cat /etc/resolv.conf #search 114.114.114.114 nameserver 180.76.76.76-- install VirtualBox yum install oraclelinux-developer-release-* wget https://yum.oracle.com/RPM-GPG-KEY-oracle-ol7 -O /etc/pki/rpm-g…...
MySQL 基础学习(3):排序查询和条件查询
MySQL 查询与条件操作:详解与技巧 在本文中,我们将探讨 MySQL 中的查询操作及其相关功能,包括别名、去重、排序查询和条件查询等,并总结一些最佳实践和注意事项。 一、使用别名(AS) 在查询中,…...
2025数学建模美赛|赛题翻译|E题
2025数学建模美赛,E题赛题翻译 更多美赛内容持续更新中......
solidity高阶 -- 继承
Solidity是一种面向区块链的智能合约编程语言,广泛应用于以太坊等区块链平台。继承是Solidity中一个非常重要的特性,它允许开发者通过创建子合约来扩展父合约的功能,从而实现代码的复用和层次化设计。本文将通过具体实例详细介绍Solidity语言…...
SpringBoot统一数据返回格式 统一异常处理
统一数据返回格式 & 统一异常处理 1. 统一数据返回格式1.1 快速入门1.2 存在问题1.3 案列代码修改1.4 优点 2. 统一异常处理 1. 统一数据返回格式 强制登录案例中,我们共做了两部分⼯作 通过Session来判断⽤⼾是否登录对后端返回数据进⾏封装,告知前端处理的结果 回顾 后…...
C语言学习强化
前言 数据的逻辑结构包括: 常见数据结构: 线性结构:数组、链表、队列、栈 树形结构:树、堆 图形结构:图 一、链表 链表是物理位置不连续,逻辑位置连续 链表的特点: 1.链表没有固定的长度…...
反馈驱动、上下文学习、多语言检索增强等 | Big Model Weekly 第55期
点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 01 A Bayesian Approach to Harnessing the Power of LLMs in Authorship Attribution 传统方法严重依赖手动特征,无法捕捉长距离相关性,限制了其有效性。最近的研究利用预训练语言模型的…...
Redis 详解
简介 Redis 的全称是 Remote Dictionary Server,它是一个基于内存的 NoSQL(非关系型)数据库,数据以 键值对 存储,支持各种复杂的数据结构 为什么会出现 Redis? Redis 的出现是为了弥补传统数据库在高性能…...
git reset (取消暂存,保留工作区修改)
出现这种情况的背景:我不小心把node_modules文件添加到暂存区了,由于文件过大,导致不能提交,所以我想恢复之前的状态,但又不想把修改的代码恢复为之前的状态,所以使用这个命令可以只恢复暂存区的状态&#…...
C++ 自定义字面量
在C11中,用户自定义字面量(User-Defined Literals)为程序员提供了前所未有的灵活性和便利性,它允许我们根据自己的需求定义字面量,从而使代码更加直观、易读且富有表现力。 什么是用户定义字面量? 在C中&…...
2024年面对不确定性
24年处在了十字路口,面对工作、家庭、生活的责任,一切变得不确定了,量子力学给了我们新的认识世界的角度,不确定性才是这个世界的底色,我们怎么选择? 不停的思考 霍金在大设计书中给出了深刻的哲学思想&a…...
Coze插件开发之基于已有服务创建并上架到扣子商店
Coze插件开发之基于已有服务创建并上架到扣子商店 在应用开发中,需要调用各种插件,以快速进行开发。但有时需要调用的插件在扣子商店里没有,那怎么办呢? 今天就来带大家快速基于已有服务创建一个新的插件 简单来讲,就是…...
Oracle 创建用户和表空间
Oracle 创建用户和表空间 使用sys 账户登录 建立临时表空间 --建立临时表空间 CREATE TEMPORARY TABLESPACE TEMP_POS --创建名为TEMP_POS的临时表空间 TEMPFILE /oracle/oradata/POS/TEMP_POS.DBF -- 临时文件 SIZE 50M -- 其初始大小为50M AUTOEXTEND ON -- 支持…...
企业微信开发009_使用WxJava企业微信开发框架_封装第三方应用企业微信开发002_并且实现多企业授权访问---企业微信开发011
继续接上一节来贴代码: 接下来看 config部分的代码,这部分代码,系统启动的时候,就会执行,从而把配置的一些,配置读取出来,创建,针对每个企业微信的,操作service. 首先看yml配置文件中配置部分: 可以先看一下demo中: 提供了一个配置的示例,当然这个是针对 企业内部自建应用 …...
机器学习 - 初学者需要弄懂的一些线性代数的概念
一、单位矩阵 在数学中,单位矩阵是一个方阵,其主对角线上的元素全为1,其余元素全为0。单位矩阵在矩阵乘法中起到类似于数字1在数值乘法中的作用,即任何矩阵与单位矩阵相乘,结果仍为原矩阵本身。 单位矩阵的定义&…...
【学术会议-第五届机械设计与仿真国际学术会议(MDS 2025) 】前端开发:技术与艺术的完美融合
重要信息 大会官网:www.icmds.net 大会时间:2025年02月28日-03月02日 大会地点:中国-大连 会议简介 2025年第五届机械设计与仿真国际学术会议(MDS 2025) 将于2025年02月28-3月02日在中国大连召开。MDS 2025将围绕“机械设计”…...
RabbitMQ 分布式高可用
文章目录 前言一、持久化与内存管理1、持久化机制2、内存控制1、命令行2、配置文件 3、内存换页4、磁盘控制 二、集群1、Erlang的分布式特性2、RabbitMQ的节点类型2.1、磁盘节点 (Disk Node)2.2、内存节点 (RAM Node) 3、构建集群3.1 普通集群3.2 镜像队列3.3、高可用实现方案3…...
海康工业相机 SDK对接 Hikvision
有C#基础的,可以参考下,直接上代码 BaseResult 来自于Nuget包,搜Rotion可以搜出来 LS.Standard.Data 海康的接口操作,要先引用相应的dll using MvCamCtrl.NET; using PCZD.Commons.Data.CameraModel; using PCZD.Data; using Sys…...
开发技巧,vue 中的动态组件的引用 component + is
在项目中很多时候有切换 tab 的场景,一般来说都是用 v-if 或者 v-show 然后根据各种条件来控制显示隐藏。 其实我们可以使用 vue 中的动态组件,也能实现这个效果 <!-- currentTab 改变时组件也改变 --> <component :is"currentTab"…...
MySQL 二进制安装(正式篇)
Author:Arsen Date:2025/01/24 官方参考文档:点击链接跳转 目录 规划下载安装管理FAQ 规划 OSMySQL Server Version备注CentOS 7.9 or Linux - Generic8.0.33(GNU libc) 2.17 下载 二进制包下载地址:https://downloads.mysql.…...
MongoDB 数据库备份和恢复全攻略
在当今数据驱动的时代,数据库的稳定运行和数据安全至关重要。MongoDB 作为一款流行的 NoSQL 数据库,以其灵活的文档模型和高扩展性备受青睐。然而,无论数据库多么强大,数据丢失的风险始终存在,因此掌握 MongoDB 的备份…...
K8S部署DevOps自动化运维平台
持续集成(CI) 持续集成强调开发人员提交了新代码之后,立刻自动的进行构建、(单元)测试。根据测试结果,我 们可以确定新代码和原有代码能否正确地集成在一起。持续集成过程中很重视自动化测试验证结果&#…...
工业相机 SDK 二次开发-Sherlock插件
本文介绍了 sherlock 连接相机时的插件使用。通过本套插件可连接海康的工业相机。 一.环境配置 1. 拷贝动态库 在用户安装 MVS 目录下按照如下路径 Development\ThirdPartyPlatformAdapter 找到目 录为 DalsaSherlock 的文件夹,根据 Sherlock 版本找到…...
分布式版本控制系统:Git
1 Git概述 Git官网:https://git-scm.com/ Git是一个免费的、开源的分布式版本控制系统,可以快速高效地处理从小型到大型的各种项目Git易于学习,占地面积小,性能极快。它具有廉价的本地库、方便的暂存区域和多个工作流分支等特性…...
