redis缓存(穿透, 雪崩, 击穿, 数据不一致, 数据并发竞争 ), 分布式锁(watch乐观锁, setnx, redission)

 


redis的watch缓存机制


WATCH 机制原理:


WATCH 机制:使用 WATCH 监视一个或多个 key , 跟踪 key 的 value 修改情况,如果有key 的 value 值在事务 EXEC 执行之前被修改了,整个事务被取消。EXEC 返回提示信息,表示
事务已经失败。
WATCH 机制使的事务 EXEC 变的有条件,事务只有在被 WATCH 的 key 没有修改的前提下才能执行。不满足条件,事务被取消。使用 WATCH 监视了一个带过期时间的键,那么即使这个键过期了,事务仍然可以正常执行.
大多数情况下,不同的客户端会访问不同的键,相互同时竞争同一 key 的情况一般都很少,watch 能很好解决数据冲突的问题。


取消watch机制


①WATCH 命令可以被调用多次。对键的监视从 WATCH 执行之后开始生效,直到调用 EXEC 为止。不管事务是否成功执行,对所有键的监视都会被取消。
②当客户端断开连接时,该客户端对键的监视也会被取消。
③UNWATCH 命令可以手动取消对所有键的监视


缓存问题
缓存穿透
按照key查询,缓存不存在,就去后端系统查找(比如DB)。
缓存穿透是指在高并发下查询key不存在的数据,会穿过缓存查询数据库。导致数据库压力过大而宕机

解决方案:

空值也缓存,缓存时间(ttl)设置短一点,或者该key对应的数据insert了之后清理缓存。(一般可用, 但不防恶意攻击)
问题:缓存太多空值占用了更多的空间

使用布隆过滤器。查询缓存前先去布隆过滤器查询 key 是否存在,如果不存在就直接返回,存在再查缓存和DB。


布隆过滤器(Bloom Filter)是1970年由布隆提出的。二进制向量, 多个随机hash映射函数(解决hash碰撞问题) 

检索元素是否在集合中。优点: 空间效率 和 查询时间 都远超过一般算法。


原理是,当一个元素被加入集合时,通过K个Hash函数将这个元素映射成一个数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。

把字符串------>位 省空间 (1或0)
不用循环------>比较位置 省时间

 


缓存雪崩
突然大量key失效或redis重启,大量访问数据库,数据库崩溃

解决方案:
1、 key的失效期分散开 不同的key设置不同的有效期
2、设置二级缓存(数据不一定一致)
3、高可用(脏读)


缓存击穿
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。则易产生问题:缓存被“击穿”

这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。

缓存过期时,恰好对这个Key有大量的并发请求,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

解决方案:
1、用分布式锁控制访问的线程
使用redis的setnx互斥锁先进行判断,这样其他线程就处于等待状态,保证不会有大并发操作去操作数据库。

2、不设超时时间,volatile-lru 但会造成写一致问题
当数据库数据发生更新时,缓存中的数据不会及时更新,这样会造成数据库中的数据与缓存中的数据的不一致,应用会从缓存中读取到脏数据。可采用延时双删策略处理,后面详细讲。


数据不一致
 缓存和DB不一致, 主从不一致.

缓存和DB的数据不一致的根源 : 数据源不一样

如何解决
强一致性很难,追求最终一致性(时间)

互联网业务数据处理的特点
高吞吐量
低延迟
数据敏感性低于金融业

时序控制是否可行?
先更新数据库再更新缓存或者先更新缓存再更新数据库
本质上不是一个原子操作,所以时序控制不可行
高并发情况下会产生不一致

保证数据的最终一致性(延时双删和TTL和脚本删除)
1、先更新数据库同时删除缓存项(key),等读的时候再填充缓存
2、2秒后再删除一次缓存项(key)
3、设置缓存过期时间 Expired Time 比如 10秒 或1小时
4、将缓存删除失败记录到日志中,利用脚本提取失败记录再次删除(缓存失效期过长 7*24)

升级方案(异步,滞后)
通过数据库的binlog来异步淘汰key,利用工具(canal)将binlog日志采集发送到MQ中,然后通过ACK机制确认处理删除缓存。


数据并发竞争
这里的并发指的是多个redis的client同时set 同一个key引起的并发问题。
多客户端(Jedis)同时并发写一个key,一个key的值是1,本来按顺序修改为2,3,4,最后是4,但是顺序变成了4,3,2,最后变成了2。

第一种方案:分布式锁+时间戳

1.整体技术方案
这种情况,主要是准备一个分布式锁,大家去抢锁,抢到锁就做set操作。
加锁的目的实际上就是把并行读写改成串行读写的方式,从而来避免资源竞争。



2.Redis分布式锁的实现

主要用到的redis函数是setnx()
用SETNX实现分布式锁
时间戳
由于上面举的例子,要求key的操作需要顺序执行,所以需要保存一个时间戳判断set顺序。

系统A key 1 {ValueA 7:00}
系统B key 1 { ValueB 7:05}

假设系统B先抢到锁,将key1设置为{ValueB 7:05}。接下来系统A抢到锁,发现自己的key1的时间戳早于缓存中的时间戳(7:00<7:05),那就不做set操作了。


第二种方案:利用消息队列

在并发量过大的情况下,可以通过消息中间件进行处理,把并行读写进行串行化。
把Redis的set操作放在队列中使其串行化,必须的一个一个执行。

Hot Key
当有大量的请求(几十万)访问某个Redis某个key时,由于流量集中达到网络上限,从而导致这个redis的服务器宕机。造成缓存击穿,接下来对这个key的访问将直接访问数据库造成数据库崩溃,或者访问数据库回填Redis再访问Redis,继续崩溃。


 


如何发现热key
1、预估热key,比如秒杀的商品、火爆的新闻等
2、在客户端进行统计,实现简单,加一行代码即可
3、如果是Proxy,比如Codis,可以在Proxy端收集
4、利用Redis自带的命令,monitor、hotkeys。但是执行缓慢(不要用)
5、利用基于大数据领域的流式计算技术来进行实时数据访问次数的统计,比如 Storm、SparkStreaming、Flink,都可。发现热点数据后可以写到zookeeper中

如何处理热Key:
1、变分布式缓存为本地缓存
      发现热key后,把缓存数据取出后,直接加载到本地缓存中。可以采用Ehcache、Guava Cache都可以,这样系统在访问热key数据时就可以直接访问自己的缓存了。(数据不要求时时一致)
2、在每个Redis主节点上备份热key数据,这样在读取时可以采用随机读取的方式,将访问压力负载到每个Redis上。
3、利用对热点数据访问的限流熔断保护措施

每个系统实例每秒最多请求缓存集群读操作不超过 400 次,一超过就可以熔断掉,不让请求缓存集群,直接返回一个空白信息,然后用户稍后会自行再次重新刷新页面之类的。(首页不行,系统友好性差)

通过系统层自己直接加限流熔断保护措施,可以很好的保护后面的缓存集群。

Big Key
大key指的是

存储的值(Value)非常大,常见场景:

热门话题下的讨论
大V的粉丝列表
序列化后的图片
没有及时处理的垃圾数据
.....
大key的影响:

大key会大量占用内存,在集群中无法均衡
Redis的性能下降,主从复制异常
在主动删除或过期删除时会操作时间过长而引起服务阻塞
如何发现大key:
1、redis-cli --bigkeys命令。可以找到某个实例5种数据类型(String、hash、list、set、zset)的最大key。
      但如果Redis 的key比较多,执行该命令会比较慢
2、获取生产Redis的rdb文件,通过rdbtools分析rdb生成csv文件,再导入MySQL或其他数据库中进行分析统计,根据            size_in_bytes统计bigkey

大key的处理:
优化big key的原则就是string减少字符串长度,list、hash、set、zset等减少成员数。
1、string类型的big key,尽量不要存入Redis中,可以使用文档型数据库MongoDB或缓存到CDN上。
      如果必须用Redis存储,最好单独存储,不要和其他的key一起存储。采用一主一从或多从。
2、单个简单的key存储的value很大,可以尝试将对象分拆成几个key-value, 使用mget获取值,这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多次操作中,降低对redis的IO影响。
      hash, set,zset,list 中存储过多的元素,可以将这些元素分拆。(常见)


以hash类型举例来说,对于field过多的场景,
可以根据field进行hash取模,生成一个新的key,
例如原来的hash_key:{filed1:value, filed2:value, filed3:value ...},可以hash取模后形成如下key:value形式
hash_key:1:{filed1:value}
hash_key:2:{filed2:value}
hash_key:3:{filed3:value}
...
取模后,将原先单个key分成多个key,
每个key filed个数为原先的1/N

 


3、删除大key时不要使用del,因为del是阻塞命令,删除时会影响性能。
4、使用 lazy delete (unlink命令)
删除指定的key(s),若key不存在则该key被跳过。但是,相比DEL会产生阻塞,该命令会在另一个线程中回收内存,因此它是非阻塞的。 这也是该命令名字的由来:仅将keys从key空间中删除,真正的数据删除会在后续异步操作。


redis> SET key1 "Hello"
"OK"
redis> SET key2 "World"
"OK"
redis> UNLINK key1 key2 key3
(integer) 2

 


 


利用Watch实现Redis乐观锁
乐观锁基于CAS(Compare And Swap)思想(比较并替换),是不具有互斥性,不会产生锁等待而消耗资源,但是需要反复的重试,但也是因为重试的机制,能比较快的响应。因此我们可以利用redis来实现乐观锁。

具体思路如下:
1、利用redis的watch功能,监控这个redisKey的状态值

2、获取redisKey的值

3、创建redis事务

4、给这个key的值+1

5、然后去执行这个事务,如果key的值被修改过则回滚,key不加1

Redis乐观锁实现秒杀


import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TestCAS {public static void main(String[] args){String redisKey = "lock";ExecutorService executorService = Executors.newFixedThreadPool(20);try {Jedis jedis = new Jedis("192.168.127.128", 6380);// 初始值jedis.set(redisKey, "0");jedis.close();} catch (Exception e) {e.printStackTrace();}for (int i = 0; i < 1000; i++) {executorService.execute(() -> {Jedis jedis1 = new Jedis("192.168.127.128", 6380);try {jedis1.watch(redisKey); //第二次 前面改过的值String redisValue = jedis1.get(redisKey);int valInteger = Integer.valueOf(redisValue);String userInfo = UUID.randomUUID().toString();// 没有秒完if (valInteger < 20) {Transaction tx = jedis1.multi();//自增tx.incr(redisKey);// watchList list = tx.exec();// 秒成功   失败返回空list而不是空if (list != null && list.size() > 0) {System.out.println("用户:" + userInfo + ",秒杀成功!当前成功人数:" + (valInteger + 1));}// 版本变化,被别人抢了。else {System.out.println("用户:" + userInfo + ",秒杀失败");}}// 秒完了else {System.out.println("已经有20人秒杀成功,秒杀结束");}} catch (Exception e) {e.printStackTrace();} finally {jedis1.close();}});}executorService.shutdown();}
}

setnx
实现原理
共享资源互斥
共享资源串行化

单应用中使用锁:(单进程多线程)
synchronized、ReentrantLock
分布式应用中使用锁:(多进程多线程)
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。
利用Redis的单线程特性对共享资源进行串行化处理

实现方式
获取锁
方式1(使用set命令实现)--推荐


/*** 使用redis的set命令实现获取分布式锁* @param lockKey  可以就是锁* @param requestId 请求ID,保证同一性  uuid+threadID* @param expireTime 过期时间,避免死锁* @return*/public  boolean getLock(String lockKey,String requestId,int expireTime) {//NX:保证互斥性// hset  原子性操作 只要lockKey有效 则说明有进程在使用分布式锁String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);if("OK".equals(result)) {return true;  // 返回OK则表示设置成功, 即获得了锁}return false;     // 该kry已存在, 设置失败, 未获得锁     }

方式2(使用setnx命令实现) -- 并发会产生问题(死锁)


public  boolean getLock(String lockKey,String requestId,int expireTime) {
    Long result = jedis.setnx(lockKey, requestId);
    if(result == 1) {
        //成功设置 进程down 永久有效  别的进程就无法获得锁
        jedis.expire(lockKey, expireTime);
        return true;
    }
    return false;
}


释放锁

方式1(del命令实现) -- 并发


 /**
* 释放分布式锁
* @param lockKey
* @param requestId
*/
public static void releaseLock(String lockKey,String requestId) {
   
    if (requestId.equals(jedis.get(lockKey))) {
        jedis.del(lockKey);  // 进入当前语句时, 锁已经过期, 且被别人获得
    }
}


问题在于如果调用jedis.del()方法的时候,这把锁已经不属于当前客户端的时候会解除他人加的锁。
那么是否真的有这种场景?答案是肯定的,比如客户端A加锁,一段时间之后客户端A解锁,在执行jedis.del()之前,锁突然过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。


方式2(redis+lua脚本实现)--推荐


 public static boolean releaseLock(String lockKey, String requestId) {
 
    String script = "if redis.call('get', KEYS[1]) == ARGV[1]"
                     + "then returnredis.call('del', KEYS[1])" 
                     + "else return 0 end";
    Object result = jedis.eval(script, Collections.singletonList(lockKey)
                                       , Collections.singletonList(requestId));
    if (result.equals(1L)) {
        return true;
    }
    return false;
}


存在问题

单机
无法保证高可用

主--从
无法保证数据的强一致性,在主机宕机时会造成锁的重复获得。

一定程度上缓解(红锁redlock)


 


无法续租
超过expireTime后,不能继续使用

本质分析
CAP模型分析
在分布式环境下不可能满足三者共存,只能满足其中的两者共存,在分布式下P不能舍弃(舍弃P就是单机了)。
所以只能是CP(强一致性模型)和AP(高可用模型)。
分布式锁是CP模型,Redis集群是AP模型。 (base)
Redis集群不能保证数据的随时一致性,只能保证数据的最终一致性。

为什么还可以用Redis实现分布式锁?
与业务有关
当业务不需要数据强一致性时,比如:社交场景,就可以使用Redis实现分布式锁
当业务必须要数据的强一致性,即不允许重复获得锁,比如金融场景(重复下单,重复转账)就不要使用
可以使用CP模型实现,比如:zookeeper和etcd。

Redisson分布式锁的使用
Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。
Redisson在基于NIO的Netty框架上,生产环境使用分布式锁。


加入jar包的依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>2.7.0</version>
</dependency>


 public class RedissonManager {
     private static Config config = new Config();
 
     //声明redisso对象
     private static Redisson redisson = null;
 
     //实例化redisson
     static{
         config.useClusterServers()
 
         // 集群状态扫描间隔时间,单位是毫秒
         .setScanInterval(2000)
 
         //cluster方式至少6个节点(3主3从,3主做sharding,3从用来保证主宕机后可以高可用)
         .addNodeAddress("redis://127.0.0.1:6379" )
         .addNodeAddress("redis://127.0.0.1:6380")
         .addNodeAddress("redis://127.0.0.1:6381")
         .addNodeAddress("redis://127.0.0.1:6382")
         .addNodeAddress("redis://127.0.0.1:6383")
         .addNodeAddress("redis://127.0.0.1:6384");
     
         //得到redisson对象
         redisson = (Redisson) Redisson.create(config);
     }
 
     //获取redisson对象的方法
     public static Redisson getRedisson(){
         return redisson;
     }
 } 

锁的获取和释放


public class DistributedRedisLock {
 
    //从配置类中获取redisson对象
    private static Redisson redisson = RedissonManager.getRedisson();
    private static final String LOCK_TITLE = "redisLock_";
 
    //加锁
    public static boolean acquire(String lockName){
        //声明key对象
        String key = LOCK_TITLE + lockName;
        //获取锁对象
        RLock mylock = redisson.getLock(key);
        //加锁,并且设置锁过期时间3秒,防止死锁的产生  uuid+threadId
        mylock.lock(2,3,TimeUtil.SECOND);
        //加锁成功
        return  true;
    }
 
    //锁的释放
    public static void release(String lockName){
        //必须是和加锁时的同一个key
        String key = LOCK_TITLE + lockName;
        //获取所对象
        RLock mylock = redisson.getLock(key);
        //释放锁(解锁)
        mylock.unlock();
    }


业务逻辑中使用分布式锁


public String discount() throws IOException{
     String key = "lock001";
     //加锁
     DistributedRedisLock.acquire(key);
     //执行具体业务逻辑
     dosoming
     //释放锁
     DistributedRedisLock.release(key);
     //返回结果
     return soming;

Redisson分布式锁的实现原理


加锁机制

如果该客户端面对的是一个redis cluster集群,他首先会根据hash节点选择一台机器。
发送lua脚本到redis服务器上,脚本如下:


"if (redis.call('exists',KEYS[1])==0) then "+       --看有没有锁
    "redis.call('hset',KEYS[1],ARGV[2],1) ; "+       --无锁 加锁  
    "redis.call('pexpire',KEYS[1],ARGV[1]) ; "+      
    "return nil; end ;" +
"if (redis.call('hexists',KEYS[1],ARGV[2]) ==1 ) then "+  --我加的锁
        "redis.call('hincrby',KEYS[1],ARGV[2],1) ; "+  --重入锁
        "redis.call('pexpire',KEYS[1],ARGV[1]) ; "+  
    "return nil; end ;" +
"return redis.call('pttl',KEYS[1]) ;"  --不能加锁,返回锁的时间 

ua的作用:保证这段复杂业务逻辑执行的原子性。
lua的解释:
KEYS[1]) : 加锁的key
ARGV[1] : key的生存时间,默认为30秒
ARGV[2] : 加锁的客户端ID (UUID.randomUUID()) + “:” + threadId)

第一段if判断语句,就是用“exists myLock”命令判断一下,如果你要加锁的那个锁key不存在的话,就进行加锁。如何加锁呢?很简单,用下面的命令:
hset myLock 8743c9c0-0795-4907-87fd-6c719a6b4586:1 1

通过这个命令设置一个hash数据结构,这行命令执行后,会出现一个类似下面的数据结构:

myLock :{"8743c9c0-0795-4907-87fd-6c719a6b4586:1":1 }

上述就代表“8743c9c0-0795-4907-87fd-6c719a6b4586:1”这个客户端对“myLock”这个锁key完成了加锁。
接着会执行“pexpire myLock 30000”命令,设置myLock这个锁key的生存时间是30秒。

锁互斥机制

那么在这个时候,如果客户端2来尝试加锁,执行了同样的一段lua脚本,会咋样呢?

很简单,第一个if判断会执行“exists myLock”,发现myLock这个锁key已经存在了。
接着第二个if判断,判断一下,myLock锁key的hash数据结构中,是否包含客户端2的ID,但是明显不是的,因为那里包含的是客户端1的ID。

所以,客户端2会获取到pttl myLock返回的一个数字,这个数字代表了myLock这个锁key的剩余生存时间。比如还剩15000毫秒的生存时间。
此时客户端2会进入一个while循环,不停的尝试加锁。

自动延时机制

只要客户端1一旦加锁成功,就会启动一个watch dog看门狗,他是一个后台线程,会每隔10秒检查一下,如果客户端1还持有锁key,那么就会不断的延长锁key的生存时间。

可重入锁机制

第一个if判断肯定不成立,“exists myLock”会显示锁key已经存在了。
第二个if判断会成立,因为myLock的hash数据结构中包含的那个ID,就是客户端1的那个ID,也就是“8743c9c0-0795-4907-87fd-6c719a6b4586:1”

此时就会执行可重入加锁的逻辑,他会用:

incrby myLock 8743c9c0-0795-4907-87fd-6c71a6b4586:1 1

通过这个命令,对客户端1的加锁次数,累加1。数据结构会变成:

myLock :{"8743c9c0-0795-4907-87fd-6c719a6b4586:1":2 }

释放锁机制
执行lua脚本如下
 


#如果key已经不存在,说明已经被解锁,直接发布(publish)redis消息
"if (redis.call('exists', KEYS[1]) == 0) then " +
            "redis.call('publish', KEYS[2], ARGV[1]); " +
            "return 1; " +
          "end;" +
# key和field不匹配,说明当前客户端线程没有持有锁,不能主动解锁。 不是我加的锁 不能解锁
          "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
            "return nil;" +
          "end; " +
# 将value减1
          "local counter = redis.call('hincrby', KEYS[1], ARGV[3],-1); " +
# 如果counter>0说明锁在重入,不能删除key
          "if (counter > 0) then " +
            "redis.call('pexpire', KEYS[1], ARGV[2]); " +
            "return 0; " +
# 删除key并且publish 解锁消息
          "else " +
            "redis.call('del', KEYS[1]); " + #删除锁
            "redis.call('publish', KEYS[2], ARGV[1]); " +
            "return 1; "+
          "end; " +
          "return nil;", 


– KEYS[1] :需要加锁的key,这里需要是字符串类型。
– KEYS[2] :redis消息的ChannelName,一个分布式锁对应唯一的一个channelName:“redisson_lockchannel{” + getName() + “}”
– ARGV[1] :reids消息体,这里只需要一个字节的标记就可以,主要标记redis的key已经解锁,再结合redis的Subscribe,能唤醒其他订阅解锁消息的客户端线程申请锁。
– ARGV[2] :锁的超时时间,防止死锁
– ARGV[3] :锁的唯一标识,也就是刚才介绍的 id(UUID.randomUUID()) + “:” + threadId

如果执行lock.unlock(),就可以释放分布式锁,此时的业务逻辑也是非常简单的。
其实说白了,就是每次都对myLock数据结构中的那个加锁次数减1。
如果发现加锁次数是0了,说明这个客户端已经不再持有锁了,此时就会用:
“del myLock”命令,从redis里删除这个key。
然后呢,另外的客户端2就可以尝试完成加锁了。

分布式锁特性
互斥性

任意时刻,只能有一个客户端获取锁,不能同时有两个客户端获取到锁。
Redis : setnx set key value NX 如果key存在就不设置
同一性

锁只能被持有该锁的客户端删除,不能由其它客户端删除。
Redis : lua 实现原子性
可重入性

持有某个锁的客户端可继续对该锁加锁,实现锁的续租
容错性

锁失效后(超过生命周期)自动释放锁(key失效),其他客户端可以继续获得该锁,防止死锁
expire 设置超时时间
set key value NX PX
 

分布式锁的实际应用
数据并发竞争

利用分布式锁+时间戳可以将处理串行化
防止库存超卖


 


  • 订单1下单前会先查看库存,库存为10,所以下单5本可以成功;
  • 订单2下单前会先查看库存,库存为10,所以下单8本可以成功;
  • 订单1和订单2 同时操作,共下单13本,但库存只有10本,显然库存不够了,这种情况称为库存超卖。
  • 可以采用分布式锁解决这个问题。


订单1和订单2都从Redis中获得分布式锁(setnx),谁能获得锁谁进行下单操作,这样就把订单系统下单的顺序串行化了,就不会出现超卖的情况了。伪码如下:

获得分布式锁
取得库存
如果大于下单数
下单
更改库存
释放锁


//加锁并设置有效期
if(redis.lock("RDL",200)){
   //判断库存
   if (orderNum<getCount()){
      //加锁成功 ,可以下单
      order(5);
      updateCount(5)  
      //释放锁
      redis,unlock("RDL");
   }  
}

注意此种方法会降低处理效率,这样不适合秒杀的场景,秒杀可以使用CAS和Redis队列的方式。 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/729529.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux安装,配置,启动HBase

Linux安装&#xff0c;配置&#xff0c;启动HBase 一、HBase安装&#xff0c;配置 1、下载HBase安装包 Hbase官方下载地址: http://archive.apache.org/dist/hbase 选择相应的版本点击下载&#xff0c;这里以2.3.5为例 # linux可以通过wget命令下载 wget https://archive…

Windows11安装FFmpeg最新版本

打开终端: 输入 winget install ffmpeg 然后输入 Y 回车后出现如下图: 正在下载FFmpeg 6.1 安装成功 测试

STM32CubeIDE基础学习-STM32CubeIDE软件配置下载器方法

STM32CubeIDE基础学习-STM32CubeIDE软件配置下载器方法 文章目录 STM32CubeIDE基础学习-STM32CubeIDE软件配置下载器方法前言第1章 配置ST-LINK下载器第2章 配置DAP下载器总结 前言 这个软件编译完之后&#xff0c;可以使用下载器进行在线下载程序或仿真调试程序&#xff0c;也…

050-WEB攻防-PHP应用文件包含LFIRFI伪协议编码算法无文件利用黑白盒

050-WEB攻防-PHP应用&文件包含&LFI&RFI&伪协议编码算法&无文件利用&黑白盒 #知识点&#xff1a; 1、文件包含-原理&分类&危害-LFI&RFI 2、文件包含-利用-黑白盒&无文件&伪协议 演示案例&#xff1a; ➢文件包含-原理&分类&am…

设计模式(工厂模式)

设计模式&#xff08;工厂模式&#xff09; 一、工厂模式介绍 在工厂模式中&#xff0c;父类决定生成示例的方式&#xff0c;但不决定所要生成的具体的类&#xff0c;具体的处理部分交给子类负责。这样就可以将生成示例的框架和生成示例的类解耦。 二、示例程序 以下示例程…

力扣-数组题

1. 两数之和 找出map中是否有target-nums[i]&#xff0c; class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int, int> hash;for(int i 0 ;i < nums.size(); i){if(hash.find(target - nums[i]) ! hash…

MongoDB安装教程(附带安装包)

目录 解压安装包 傻瓜式安装 选择安装目录 安装完成 启动验证 验证成功 解压安装包 选择所需要的进行安装 傻瓜式安装 选择安装目录 安装完成 启动验证 mongod --dbpathD:\mongodb\data\db 验证成功

VUE_自适应布局lib-flexible+postcss-pxtorem、lib-flexible + postcss-px2rem,nuxt页面自适配

lib-flexible postcss-pxtorem适配 我采用的是flexable.js和postcss-pxtorem。我一开始用的是postcss-px2rem后来发现和nuxt引入公共css的时候发生了冲突所以改用了postcss-pxtorem。 安装依赖 npm i lib-flexible -S npm install postcss-pxtorem --save 1、lib-flexible.…

FreeRTOS学习笔记——列表和列表项及其API函数讲解

目录 精华总结&#xff1a; 列表和列表项 架构脑图&#xff1a; 列表和列表项的简介 列表的结构 列表项 迷你列表项 列表和列表项的关系 FreeRTOS 列表和列表项相关 API 函数 架构脑图&#xff1a; 列表和列表项相关 API 函数 函数 vListInitialise() 函数 vListIni…

IR 召回测试数据集(中文测试集)——T2Ranking

文章排名包括两个阶段&#xff1a;文章检索和文章重排序&#xff0c;这对信息检索&#xff08;IR&#xff09;领域的学术界和业界来说都是重要而具有挑战性的课题。然而&#xff0c;常用的文章排名数据集通常集中在英语语言上。对于非英语场景&#xff0c;如中文&#xff0c;现…

云犀首款AI直播调参大模型重磅发布,智能破解直播三大难!

3月7日晚&#xff0c;云犀重磅发布首款AI大模型——云犀墨子AI直播调参大模型&#xff01;据悉云犀墨子是一款懂直播的调参大模型&#xff0c;这项技术深度融合了机器学习、计算机视觉以及进步的图像处理算法&#xff0c;赋予直播间画面AI智能调控能力。 此次云犀墨子AI直播调参…

Imagination大学计划 rv-fpga实验安装教程及注意事项

1. 实验环境安装 1.1 vivado 2019.2 网盘链接&#xff1a;Vivado_2019.2.tar.gz_免费高速下载|百度网盘-分享无限制 (baidu.com) 安装教程&#xff1a; 网盘中vivado文件夹下载到本地后解压打开&#xff0c;导航到下图&#xff0c;打开xsetup.exe文件。如果打开后有小弹窗直接…

论文阅读之Multimodal Chain-of-Thought Reasoning in Language Models

文章目录 简介摘要引言多模态思维链推理的挑战多模态CoT框架多模态CoT模型架构细节编码模块融合模块解码模块 实验结果总结 简介 本文主要对2023一篇论文《Multimodal Chain-of-Thought Reasoning in Language Models》主要内容进行介绍。 摘要 大型语言模型&#xff08;LLM…

重生奇迹mu战士大师技能加点怎么加

1、在重生奇迹MU中&#xff0c;战士大师的技能加点需要根据个人的游戏风格和需求来决定。一般来说&#xff0c;战士大师可以优先加点力量和体力&#xff0c;以增加攻击和生存能力。同时&#xff0c;可以适当加点敏捷来提高闪避和命中率。 2、在技能方面&#xff0c;可以根据个人…

数组的内存执行原理

一.Java内存分配介绍 JVM虚拟机会在内存中执行程序 java内存分配介绍 方法区&#xff0c;栈&#xff0c;堆 首先将编译过后的.class文件送入方法区中。当类开始运行时将方法调入栈内存中&#xff0c;变量也是属于方法的&#xff0c;因此同方法一起进入栈内存中。当main方法要…

Word背景图片设置,提升文章美观度的4个小技巧!

“我才刚开始使用Word&#xff0c;想问问大家Word中背景图片应该怎么设置呢&#xff1f;有什么比较好用的设置方法可以分享一下吗&#xff1f;” 在日常办公中&#xff0c;我们经常需要使用Word来对文件进行处理。在编写Word时&#xff0c;如果给文档加入背景图片&#xff0c;会…

[pcie]通过sysfs访问PCI设备资源

通过sysfs访问PCI设备资源 参考文档&#xff1a; https://www.kernel.org/doc/html/next/translations/zh_CN/PCI/sysfs-pci.html

直播相机,AI纪元!云犀首款AI直播相机重磅发布!

3月7日晚&#xff0c;云犀重磅发布首款AI直播相机-云犀智瞳Pro&#xff01;据悉该相机内置了云犀首款AI直播调参大模型&#xff0c;能够通过AI分析智能调节直播间参数&#xff0c;赋予直播间画面AI智能调控能力。 云犀AI直播相机&#xff0c;让直播更简单 此次云犀AI直播相机的…

企业业务在ERP系统中的流程分析

ERP系统能将企业的信息流、资金流、物流等进行统一管控&#xff0c;并且可以通过它优化企业的业务流程。本文主要介绍企业的一些业务在ERP中的流程设置。 首先是销售管理业务&#xff0c;销售管理以销售部门为核心&#xff0c;仓库、财务部门为辅助&#xff0c;偶尔需要生产、质…

mysql中insert … select锁范围

1、执行 insert … select 的时候&#xff0c;对目标表也不是锁全表&#xff0c;而是只锁住需要访问的资源。 例如&#xff0c; CREATE TABLE t (id int(11) NOT NULL AUTO_INCREMENT,c int(11) DEFAULT NULL,d int(11) DEFAULT NULL,PRIMARY KEY (id),UNIQUE KEY c (c) ) EN…