redis持久化、主从和哨兵架构

news2024/11/26 2:29:02

一、redis持久化

1、RDB快照(snapshot)

redis配置RDB存储模式,修改redis.conf文件如下配置:

# 在300s内有100个或者以上的key被修改就会把redis中的数据持久化到dump.rdb文件中
# save 300 100

# 配置数据存放目录(现在指定的是redis目录下的data文件夹)
dir ./data

# 配置rdb文件存储文件名
dbfilename dump.rdb

 也可以在连接redis时手动输入save或者bgsave命令都会将所有redis内存快照生成到一个新的dump.rdb文件,并覆盖原来的rdb文件

bgsave写时复制(COW)机制

主线程在执行redis命令,在内存中生成副本的同时由主线程fork出来的子进程开始读取redis内存中的数据并且写到rdb文件中,fork出来的bgsave子进程和主线程互不影响(redis后台生成rdb文件是采用bgsave方式)

save和bgsave

  1. save是和主线程同步执行,bgsave是异步执行
  2. save会阻塞其它命令,bgsave在主线程fork时会有短暂的阻塞
  3. save不会额外消耗内存,bgsave不会阻塞客户端命令

命令

save

bgsave

IO类型

同步

异步

是否阻塞redis其它命令

否(在生成子进程执行调用fork函数时会有短暂阻塞)

复杂度

O(n)

O(n)

优点

不会消耗额外内存

不阻塞客户端命令

缺点

阻塞客户端命令

需要fork子进程,消耗内存

2、AOF(append-only file)

rdb的缺点也非常明显,不是实时持久化数据到rdb文件,如果系统宕机,redis会丢失最近时间的数据。AOF持久化是将修改的每一条指令报存到appendonly.aof文件中(和mysql的redolog一样先写os cache操作系统缓存,每隔一段时间sync到磁盘)

开启aof

修改redis.conf文件如下配置:

# 开启aof
appendonly yes
# aof文件名
appendfilename "appendonly.aof"

# aof三种策略
# 每次有新命令追加到 AOF 文件时就执行一次 fsync ,非常慢,也非常安全。
# appendfsync always
# 每秒 fsync 一次,足够快,并且在故障时只会丢失 1 秒钟的数据。
# appendfsync everysec
# 从不 fsync ,将数据交给操作系统来处理。更快,也更不安全的选择。
# appendfsync no

执行set gaorufeng 123 ex 100 命令生成的aof文件如下(星号后面的数字代表命令有多少个参数,$号后面的数字代表这个参数有几个字符,如果执行带过期时间的set命令,aof文件里记录的是并不是执行的原始命令,而是记录key过期的时间戳)

*5
$3
SET
$9
gaorufeng
$3
123
$4
PXAT
$13
1693895615978
AOF重写 

比如一条自增命令,在生成aof文件就会生成多条指令,incr count命令的aof文件如下:

*2
$4
incr
$5
count
*2
$4
incr
$5
count
*2
$4
incr
$5
count
*2
$4
incr
$5
count

重写的作用旨在减少指令的存储,可以配置aof让redis重写,修改redis.conf文件如下配置

# 当前文件是之前文件大小的两倍(设置100),自动触发重写
auto-aof-rewrite-percentage 100
# aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大
auto-aof-rewrite-min-size 64mb

 redis客户端执行bgrewriteaof手动重写aof,此时命令如下:

*3
$3
SET
$5
count
$1
4

aof方式存储数据完整,rdb存储数据文件小,故启动redis时当选rdb恢复内存数据,二进制恢复数据快 

命令

RDB

AOF

启动优先级

体积

恢复速度

数据安全性

容易丢数据

根据策略决定

3、混合持久化

# 开启混合持久化
aof-use-rdb-preamble yes

在开启aof的前提下,aof重写前内存做rdb快照处理,并且将rdb快照内容和增量的aof修改内存数据的命令存在一起

Redis数据备份策略:

  1. 写crontab定时调度脚本,每小时都copy一份rdb或aof的备份到一个目录中去,仅仅保留最近48小时的备份
  2. 每天都保留一份当日的数据备份到一个目录中去,可以保留最近1个月的备份
  3. 每次copy备份的时候,都把太旧的备份给删了
  4. 每天晚上将当前机器上的备份复制一份到其他机器上,以防机器损坏

二、redis主从架构

redis主从架构搭建,配置从节点步骤:

1、进入到redis目录下,创建数据存储目录
mkdir data
cd data
mkdir 6379
mkdir 6380
mkdir 6381

2、copy不同配置文件
cp -r redis.conf redis.conf.6379
cp -r redis.conf redis.conf.6380
cp -r redis.conf redis.conf.6381

3、(6379为主节点,其余为从节点)修改配置文件
vim redis.conf.6379
在6379配置文件中修改如下
port 6379
daemonize yes
protected-mode no
pidfile /var/run/redis_6379.pid
logfile "6379.log"
dir /usr/local/redis/redis-6.2.7/data/6379
# 需要注释掉bind
# bind 127.0.0.1(bind绑定的是自己机器网卡的ip,如果有多块网卡可以配多个ip,代表允许客户端通过机器的哪些网卡ip去访问,内网一般可以不配置bind,注释掉即可)

4、配置从节点6380
vim redis.conf.6380
在6380配置文件中修改如下
port 6380
daemonize yes
protected-mode no
pidfile /var/run/redis_6380.pid
logfile "6380.log"
dir /usr/local/redis/redis-6.2.7/data/6380
# 从本机6379的redis实例复制数据,Redis 5.0之前使用slaveof
replicaof 192.168.146.128 6379
# 配置从节点只读
replica-read-only yes

5、配置从节点6381
vim redis.conf.6381
在6381配置文件中修改如下
port 6381
daemonize yes
protected-mode no
pidfile /var/run/redis_6381.pid
logfile "6381.log"
dir /usr/local/redis/redis-6.2.7/data/6381
# 从本机6379的redis实例复制数据,Redis 5.0之前使用slaveof
replicaof 192.168.146.128 6379
# 配置从节点只读
replica-read-only yes

6、先启动主节点6379,后启动从节点6380、6381
src/redis-server redis.conf.6379
src/redis-server redis.conf.6380
src/redis-server redis.conf.6381

7、连接从节点查看是否能获取主节点数据
src/redis-cli -p 6379
src/redis-cli -p 6381
Redis主从工作原理 

如果你为master配置了一个slave,不管这个slave是否是第一次连接上Master,它都会发送一个PSYNC命令给master请求复制数据。

master收到PSYNC命令后,会在后台进行数据持久化通过bgsave生成最新的rdb快照文件,持久化期间,master会继续接收客户端的请求,它会把这些可能修改数据集的请求缓存在内存中。当持久化进行完毕以后,master会把这份rdb文件数据集发送给slave,slave会把接收到的数据进行持久化生成rdb,然后再加载到内存中。然后,master再将之前缓存在内存中的命令发送给slave。

当master与slave之间的连接由于某些原因而断开时,slave能够自动重连Master,如果master收到了多个slave并发连接请求,它只会进行一次持久化,而不是一个连接一次,然后再把这一份持久化的数据发送给多个并发连接的slave。

主从复制(全量复制)流程图:

数据部分复制

当master和slave断开重连后,一般都会对整份数据进行复制。但从redis2.8版本开始,redis改用可以支持部分数据复制的命令PSYNC去master同步数据,slave与master能够在网络连接断开重连后只进行部分数据复制(断点续传)。

master会在其内存中创建一个复制数据用的缓存队列,缓存最近一段时间的数据,master和它所有的slave都维护了复制的数据下标offset和master的进程id,因此,当网络连接断开后,slave会请求master继续进行未完成的复制,从所记录的数据下标开始。如果master进程id变化了,或者从节点数据下标offset太旧,已经不在master的缓存队列里了,那么将会进行一次全量数据的复制。

 主从复制(部分复制,断点续传)流程图:

如果有很多从节点,为了缓解主从复制风暴(多个从节点同时复制主节点导致主节点压力过大),可以做如下架构,让部分从节点与从节点(与主节点同步)同步数据

Jedis连接代码示例:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
public class JedisSingleTest {

	public static void main(String[] args) {
		JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
		jedisPoolConfig.setMaxTotal(20);
		jedisPoolConfig.setMaxIdle(10);
		jedisPoolConfig.setMinIdle(5);
		
		// timeout,这里既是连接超时又是读写超时,从Jedis 2.8开始有区分connectionTimeout和soTimeout的构造函数
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, "192.168.146.128", 6379, 3000, null);
        
        Jedis jedis = null;
        try {
            //从redis连接池里拿出一个连接执行命令
            jedis = jedisPool.getResource();

            System.out.println(jedis.set("single", "gao"));
            System.out.println(jedis.get("single"));

            //管道示例
            //管道的命令执行方式:cat redis.txt | redis-cli -h 127.0.0.1 -a password - p 6379 --pipe
            Pipeline pl = jedis.pipelined();
            for (int i = 0; i < 10; i++) {
                pl.incr("pipelineKey");
                pl.set("gao" + i, "gao");
            }
            List<Object> results = pl.syncAndReturnAll();
            System.out.println(results);

            //lua脚本模拟一个商品减库存的原子操作
            //lua脚本命令执行方式:redis-cli --eval /tmp/test.lua , 10
            jedis.set("product_count_10016", "15");  //初始化商品10016的库存
            String script = " local count = redis.call('get', KEYS[1]) " +
                            " local a = tonumber(count) " +
                            " local b = tonumber(ARGV[1]) " +
                            " if a >= b then " +
                            "   redis.call('set', KEYS[1], a-b) " +
                            "   return 1 " +
                            " end " +
                            " return 0 ";
            Object obj = jedis.eval(script, Arrays.asList("product_count_10016"), Arrays.asList("10"));
            System.out.println(obj);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。
            if (jedis != null)
                jedis.close();
        }
	}
}

管道(Pipeline)

客户端可以一次性发送多个请求而不用等待服务器的响应,待所有命令都发送完后再一次性读取服务的响应,这样可以极大的降低多条命令执行的网络传输开销,管道执行多条命令的网络开销实际上只相当于一次命令执行的网络开销。需要注意到是用pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并不是打包的命令越多越好。

pipeline中发送的每个command都会被server立即执行,如果执行失败,将会在此后的响应中得到信息;也就是pipeline并不是表达“所有command都一起成功”的语义,管道中前面命令失败,后面命令不会有影响,继续执行。

三、redis哨兵高可用架构

sentinel哨兵是特殊的redis服务,不提供读写服务,主要用来监控redis实例节点。

哨兵架构下client端第一次从哨兵找出redis的主节点,后续就直接访问redis的主节点,不会每次都通过sentinel代理访问redis的主节点,当redis的主节点发生变化,哨兵会第一时间感知到,并且将新的redis主节点通知给client端(这里面redis的client端一般都实现了订阅功能,订阅sentinel发布的节点变动消息)

 基于主从架构的redis哨兵架构搭建步骤:

1、复制一份sentinel.conf文件
cp sentinel.conf sentinel-26379.conf

2、将相关配置修改为如下值:
port 26379
daemonize yes
pidfile "/var/run/redis-sentinel-26379.pid"
logfile "26379.log"
dir "/usr/local/redis-5.0.3/data"
# sentinel monitor <master-redis-name> <master-redis-ip> <master-redis-port> <quorum>
# quorum是一个数字,指明当有多少个sentinel认为一个master失效时(值一般为:sentinel总数/2 + 1),master才算真正失效
sentinel monitor mymaster 192.168.146.128 6379 2   # mymaster这个名字随便取,客户端访问时会用到

3、启动sentinel哨兵实例
src/redis-sentinel sentinel-26379.conf

4、查看sentinel的info信息
src/redis-cli -p 26379
127.0.0.1:26379>info
可以看到Sentinel的info里已经识别出了redis的主从

5、可以自己再配置两个sentinel,端口26380和26381,注意上述配置文件里的对应数字都要修改

sentinel集群都启动完毕后,会将哨兵集群的元数据信息写入所有sentinel的配置文件里去(追加在文件的最下面),我们查看下如下配置文件sentinel-26379.conf,如下所示:

sentinel known-replica mymaster 192.168.146.128 6380 #代表redis主节点的从节点信息
sentinel known-replica mymaster 192.168.146.128 6381 #代表redis主节点的从节点信息
sentinel known-sentinel mymaster 192.168.146.128 26381 c3c89b0c5d1b81f5e9487ae17260c8fbfe477c2e  #代表感知到的其它哨兵节点
sentinel known-sentinel mymaster 192.168.146.128 26380 b3d47687e6b2d6d569b0c6a53a1b92be5c442c5c  #代表感知到的其它哨兵节点

当redis主节点如果挂了,哨兵集群会重新选举出新的redis主节点,同时会修改所有sentinel节点配置文件的集群元数据信息,比如6379的redis如果挂了,假设选举出的新主节点是6380,则sentinel文件里的集群元数据信息会变成如下所示:

sentinel known-replica mymaster 192.168.146.128 6380 #代表主节点的从节点信息
sentinel known-replica mymaster 192.168.146.128 6379 #代表主节点的从节点信息
sentinel known-sentinel mymaster 192.168.146.128 26381 c3c89b0c5d1b81f5e9487ae17260c8fbfe477c2e  #代表感知到的其它哨兵节点
sentinel known-sentinel mymaster 192.168.146.128 26379 af83ca977cadbe5842472fea925c85906c26e1e0  #代表感知到的其它哨兵节点

同时还会修改sentinel文件里之前配置的mymaster对应的6379端口,改为6380

sentinel monitor mymaster 192.168.146.128 6381 2

 当6379的redis实例再次启动时,哨兵集群根据集群元数据信息就可以将6379端口的redis节点作为从节点加入集群

哨兵的Jedis连接代码:

public class JedisSentinelTest {
    public static void main(String[] args) throws IOException {

        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(20);
        config.setMaxIdle(10);
        config.setMinIdle(5);

        String masterName = "mymaster";
        Set<String> sentinels = new HashSet<String>();
        sentinels.add(new HostAndPort("192.168.0.60",26379).toString());
        sentinels.add(new HostAndPort("192.168.0.60",26380).toString());
        sentinels.add(new HostAndPort("192.168.0.60",26381).toString());
        //JedisSentinelPool其实本质跟JedisPool类似,都是与redis主节点建立的连接池
        //JedisSentinelPool并不是说与sentinel建立的连接池,而是通过sentinel发现redis主节点并与其建立连接
        JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, sentinels, config, 3000, null);
        Jedis jedis = null;
        try {
            jedis = jedisSentinelPool.getResource();
            System.out.println(jedis.set("sentinel", "gao"));
            System.out.println(jedis.get("sentinel"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。
            if (jedis != null)
                jedis.close();
        }
    }
}

1、引入相关依赖:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-pool2</artifactId>
</dependency>

springboot项目核心配置:

server:
  port: 8080

spring:
  redis:
    database: 0
    timeout: 3000
    sentinel:    #哨兵模式
      master: mymaster #主服务器所在集群名称
      nodes: 192.168.146.128:26379,192.168.146.128:26380,192.168.146.128:26381
      lettuce:
        pool:
          max-idle: 50
          min-idle: 10
          max-active: 100
          max-wait: 1000

访问代码:

@RestController
public class IndexController {

    private static final Logger logger = LoggerFactory.getLogger(IndexController.class);

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 测试节点挂了哨兵重新选举新的master节点,客户端是否能动态感知到
     * 新的master选举出来后,哨兵会把消息发布出去,客户端实际上是实现了一个消息监听机制,
     * 当哨兵把新master的消息发布出去,客户端会立马感知到新master的信息,从而动态切换访问的masterip
     *
     * @throws InterruptedException
     */
    @RequestMapping("/test_sentinel")
    public void testSentinel() throws InterruptedException {
        int i = 1;
        while (true){
            try {
                stringRedisTemplate.opsForValue().set("gao"+i, i+"");
                System.out.println("设置key:"+ "gao" + i);
                i++;
                Thread.sleep(1000);
            }catch (Exception e){
                logger.error("错误:", e);
            }
        }
    }
}

StringRedisTemplate与RedisTemplate详解

spring 封装了 RedisTemplate 对象来进行对redis的各种操作,它支持所有的 redis 原生的 api。在RedisTemplate中提供了几个常用的接口方法的使用,分别是:

private ValueOperations<K, V> valueOps;
private HashOperations<K, V> hashOps;
private ListOperations<K, V> listOps;
private SetOperations<K, V> setOps;
private ZSetOperations<K, V> zSetOps;

RedisTemplate中定义了对5种数据结构操作

redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForZSet();//操作有序set

StringRedisTemplate继承自RedisTemplate,也一样拥有上面这些操作。

StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。

RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。

Redis客户端命令对应的RedisTemplate中的方法列表:

String类型结构

Redis

RedisTemplate rt

set key value

rt.opsForValue().set("key","value")

get key

rt.opsForValue().get("key")

del key

rt.delete("key")

strlen key

rt.opsForValue().size("key")

getset key value

rt.opsForValue().getAndSet("key","value")

getrange key start end

rt.opsForValue().get("key",start,end)

append key value

rt.opsForValue().append("key","value")

Hash结构

hmset key field1 value1 field2 value2...

rt.opsForHash().putAll("key",map) //map是一个集合对象

hset key field value

rt.opsForHash().put("key","field","value")

hexists key field

rt.opsForHash().hasKey("key","field")

hgetall key

rt.opsForHash().entries("key")  //返回Map对象

hvals key

rt.opsForHash().values("key") //返回List对象

hkeys key

rt.opsForHash().keys("key") //返回List对象

hmget key field1 field2...

rt.opsForHash().multiGet("key",keyList)

hsetnx key field value

rt.opsForHash().putIfAbsent("key","field","value"

hdel key field1 field2

rt.opsForHash().delete("key","field1","field2")

hget key field

rt.opsForHash().get("key","field")

List结构

lpush list node1 node2 node3...

rt.opsForList().leftPush("list","node") 

rt.opsForList().leftPushAll("list",list) //list是集合对象

rpush list node1 node2 node3...

rt.opsForList().rightPush("list","node") 

rt.opsForList().rightPushAll("list",list) //list是集合对象

lindex key index

rt.opsForList().index("list", index)

llen key

rt.opsForList().size("key")

lpop key

rt.opsForList().leftPop("key")

rpop key

rt.opsForList().rightPop("key")

lpushx list node

rt.opsForList().leftPushIfPresent("list","node")

rpushx list node

rt.opsForList().rightPushIfPresent("list","node")

lrange list start end

rt.opsForList().range("list",start,end)

lrem list count value

rt.opsForList().remove("list",count,"value")

lset key index value

rt.opsForList().set("list",index,"value")

Set结构

sadd key member1 member2...

rt.boundSetOps("key").add("member1","member2",...)

rt.opsForSet().add("key", set) //set是一个集合对象

scard key

rt.opsForSet().size("key")

sidff key1 key2

rt.opsForSet().difference("key1","key2") //返回一个集合对象

sinter key1 key2

rt.opsForSet().intersect("key1","key2")//同上

sunion key1 key2

rt.opsForSet().union("key1","key2")//同上

sdiffstore des key1 key2

rt.opsForSet().differenceAndStore("key1","key2","des")

sinter des key1 key2

rt.opsForSet().intersectAndStore("key1","key2","des")

sunionstore des key1 key2

rt.opsForSet().unionAndStore("key1","key2","des")

sismember key member

rt.opsForSet().isMember("key","member")

smembers key

rt.opsForSet().members("key")

spop key

rt.opsForSet().pop("key")

srandmember key count

rt.opsForSet().randomMember("key",count)

srem key member1 member2...

rt.opsForSet().remove("key","member1","member2",...)

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

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

相关文章

【前端】场景题:如何在ul标签中插入多个节点 使用文档片段

直接插入的问题&#xff1a;会回流多次。每插入一次li就会回流一次&#xff0c;消耗性能。 这里可以使用文档片段来解决这个问题。 // 创建文档片段 let node document.createDocumentFragment()DocumentFragment节点存在于内存中&#xff0c;并不在DOM中&#xff0c;所以将子…

VR农学虚拟仿真情景实训教学演示

首先&#xff0c;VR农学虚拟仿真情景实训教学提供了更为真实的实践环境。传统的农学实训往往受制于时间、空间和资源的限制&#xff0c;学生只能通过观察或简单的模拟来学习农业知识和技能。而借助虚拟现实技术&#xff0c;学生可以进入虚拟农场&#xff0c;与各种农作物、工具…

直播软件app开发流程全解析

直播软件app开发是当今互联网行业中备受瞩目的领域。随着移动用户的爆发式增长和即时互动的需求日益增加&#xff0c;开发一款高质量的直播应用已经成为各个企业和个人创作者追逐的目标。本文将深入探讨直播软件app开发的全过程&#xff0c;为您揭示开发直播应用的关键步骤&…

uni-app语音转文字功能demo(同声传译)

目录 首先去微信开发者官网申请一下同声传译的插件 微信公众平台 在文件中开始引用&#xff1a; 首先去微信开发者官网申请一下同声传译的插件 微信公众平台 后续使用的时候可以看详情里面的信息进行使用 在文件中开始引用&#xff1a; 注意&#xff01;&#xff01;在这个…

【RuoYi移动端】uni-app中通过vuex的store来实现全局变量的修改和读取

一、在store文件中新建csjVar.js文件 const csjVar {csjMess: [{aaa:"ok"},{bbb:"no"}] } export default csjVar 二、修改store文件中新建index.js文件 import Vue from vue import Vuex from vuex import user from /store/modules/user import gette…

对于array的.toLocaleString()与.flat()区别

最近都使用到这两个方法&#xff0c;对于array。记录下具体区别 先是他们的简介,我这里用的就是string的.toLocaleString() &#xff0c;因为array的就是分别去调用里面的.toLocaleString() 在拼接成 string.toLocaleString() string[].toLocaleString()方法是一个JavaScrip…

解决Maven依赖下载问题:从阿里云公共仓库入手

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

Vue 2 条件渲染

条件渲染相关的指令有哪些&#xff1f; v-if、v-else、v-else-ifv-show v-if 的作用 <div v-if"expression"></div>v-if 根据表达式 expression 返回的值是否为 truthy 来决定其内容是否被渲染。 Vue还实现了 v-else 和 v-else-if&#xff1a; <d…

北斗提供关键技术支撑,车辆智能监管将迎来广阔发展前景

随着车辆数量的快速增长和道路交通压力的持续增加&#xff0c;如何保障交通安全和提升出行效率成为了亟待解决的问题。而车辆智能监管作为一种基于现代信息技术的管理方式&#xff0c;具有实时监控、数据分析和智能预警等优势&#xff0c;可以提高交通管理的精细化水平&#xf…

ClickHouse进阶(九):Clickhouse数据查询-3

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术,IT贫道_Apache Doris,大数据OLAP体系技术栈,Kerberos安全认证-CSDN博客 &#x1f4cc;订阅…

node socket.io

装包&#xff1a; yarn add socket.io node后台&#xff1a; const express require(express) const http require(http) const socket require(socket.io) const { getUserInfoByToken } require(../../utils/light/tools)let app express() const server http.createS…

武汉芯源半导体CW32F030系列MCU在电焊机的应用

随着工业技术的发展&#xff0c;单片机在许多领域都发挥了重要的作用。在电焊机中应用单片机&#xff0c;通过编写特定的程序&#xff0c;可以实现自动化控制、提高焊接质量和效率。 电焊机是一种用于金属焊接的设备&#xff0c;利用电弧热将金属熔化实现焊接。电焊机主要由电源…

Flutter实现CombineExecutor进行多个异步分组监听,监听第一个异步执行的开始和最后一个异步执行结束时机。

1.场景 我们在调用接口时&#xff0c;很多时候会同时调用多个接口&#xff0c;接口都是异步执行&#xff0c;我们很难知道调用的多个接口哪个会最后执行完成&#xff0c;我们有时候需要对最后一个接口执行完成的时机监听&#xff0c;所以基于该需求&#xff0c;设计了CombineE…

反序列化漏洞复现(typecho)

文章目录 执行phpinfogetshell 执行phpinfo 将下面这段代码复制到一个php文件&#xff0c;命名为typecho_1.0-14.10.10_unserialize_phpinfo.php&#xff0c;代码中定义的类名与typecho中的类相同&#xff0c;是它能识别的类&#xff1a; <?php class Typecho_Feed{const…

IPv6改造深化之路

01 IPv6改造问题及整体改造思路 随着“十四五”期间国家政策对IPv6深化改造及规模部署的推动&#xff0c;在IPv6改造过程中出现了越来越多的系统性问题&#xff0c;如图1所示。 图1 关于IPv6改造的各种疑问所有跨设备通信的IT软硬件系统均需要处理IP地址&#xff0c;各领域均需…

LeetCode —— 复写零(双指针)

题目链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目解析 将数组中出现的每个零复写一遍&#xff0c;然后将其他元素向右平移&#xff0c;数组长度不能改变。 法一&#xff1a;使用额外空间的做法 class Solution { public:void duplica…

以指标管理为抓手,亿信华辰助力锦州银行打造全行级数据应用

随着金融数字化转型的持续深入&#xff0c;数据智能的业务化应用等正逐步受到行业关注。锦州银行携手亿信华辰&#xff0c;开展全行级指标管理体系建设&#xff0c;实现指标数据统一管理、统一标准、统一来源、统一汇总、统一加工、统一呈现&#xff0c;以及持续推动基础数据治…

Day56|动态规划part16:647. 回文子串、516. 最长回文子序列、动态规划总结篇

647. 回文子串 leetcode链接&#xff1a;力扣题目链接 视频链接&#xff1a;动态规划&#xff0c;字符串性质决定了DP数组的定义 | LeetCode&#xff1a;647.回文子串 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。回文字符串 是正着读和倒过…

Mysql--技术文档--索引-《索引为什么查找数据快?》-超底层详细说明索引

索引的概念 在MySQL中&#xff0c;索引是一种数据结构&#xff0c;它被用于快速查找、读取或插入数据。索引能够极大地提高数据库查询的速度。 索引的工作方式类似于图书的索引。如果你想在图书馆找到一本书&#xff0c;你可以按照书名进行查找。书名就像是一个索引&#xf…

0012Java程序设计-springboot基于微信小程序的校园智慧帮系统的设计与实现

摘要目录相关技术2.1 MySQL数据库2.2 SpringBoot框架2.3 uniapp框架2.4 B/S架构 系统设计系统实现开发环境 摘要 随着移动互联网高速发展&#xff0c;手机、移动智能终端设备在生活中有着越来越重要的地位。在高校推崇以人为本的今天&#xff0c;也逐渐重视“移动互联网”技术…