Redis分布式系统:集群

"还不如留给花园,多一瞬色彩~" 


        当我们聊到“集群”这一个词,我们脑中构想出的画面,一定是多台机器,构成的分布式系统,这可以被称为一个“集群”。其实,在前篇的哨兵机制下,奇数个监控哨兵,以及多组主从结构的数节点所构成的大的环境,就是一个“广义的集群”。

        但,在Redis中“集群”一词是有所区别的,在Redis中有一种模式也叫做“集群”,这种“狭义的集群”,主要是为了解决存储空间不足的问题。

——前言

Redis集群模式

        哨兵机制(模式)能够提高了系统的高可用性,能够对数据节点的性能进行有效监控。但,哨兵机制下,实质上仍然是主从节点存储整个数据集,存储的压力完完全全压在了主节点上,哨兵节点仅仅是作为一种警示器,并在数据节点出现问题时,做出一定的恢复操作。

        在现如今大数据“爆棚”的时代,如何获取足够大的空间?最有效、最直接的方法就是增加机器,所谓 "⼤数据" 的核⼼, 其实就是⼀台机器搞不定了, ⽤多台机器来搞。

        Redis 的集群就是在上述的思路之下,引入了多组Master/Slave,每一组存储数据全集的⼀部分, 从⽽构成⼀个更⼤的整体, 称为 Redis 集群。例如,我们现如今由1TB 数据,现在我们有三组

Master/Slave,每一组存储着1TB中的1/3,从而能够减少各组的存储压力。

        由此,只要公司有足够多台机器,那么存储任意大小的数据都不在话下。


 

Redis数据分片        

        那么现在,我们的问题就在于,如何将要存储的数据进行划分呢?当给的一个数据Key,我们需要知道着Key应该被存储在哪一个分片上,读取时,又应该去哪一个分片上读取。围绕这个问题,我们能够很直观地想到使用 “哈希思想” 来解决这个问题。

        在Redis中,每一组节点可以被称为“片”。现目前,有三种主流的分片方式。

(1) 哈希求余

        借鉴了哈希表的基本思想:借助hash函数可以把一个key,映射到整数,再针对数组长度求得下表。

        例如,现在我们有三个分片,编号分别为0、1、2,此时我们得到一个key值,并通过哈希算法(比如使用: md5),再将这个结果%3,假设结果为0,那么这个key就应当被放在编号为0的分片上。        

哈希求余十分简单高效,我们可以通过精密设计哈希函数,让数据分布更加均匀

        然而,集群一旦进⾏扩容, N 改变了, 原有的映射规则被破坏,就需要让节点之间的数据相互传输,重新进行编排,以满足新的映射规则。此时,我们遍历所有分片中的数据,并通过重新的映射规则,将这些数据搬运到 新的分片之中。这巨大的搬运工作,无疑是耗时费力的。

(2) ⼀致性哈希算法

        为了降低搬运的工作,能够更⾼效扩容, 业界提出了 "⼀致性哈希算法"。

        哈希求余取得的值经过规则映射后,放到不同的分片之中,一旦规则发生改变,它们的在新的映射规则里,就会被放到新的分片之中。究其原因就在于,分片中的key值,在新的映射规则里,不再属于同组了,而是交叉的。存储在0号分片的数据,在新的规则下需要去到1号分片,而其他放在0号的数据,在新的规则下又得放在2号分片当中。

        在"⼀致性哈希算法"中,把集群中的地址空间映射到了一个逻辑上的圆环上,并通过hash算法将key值的结果放到对应的分片范围之中:        

        在这种情况下,出现一个新的分片,那么它需要在这个圆环中,选择一块新的范围,作为自己的分片管理范围。        

        此时, 只需要把 0 号分⽚上的部分数据,搬运给 3 号分⽚即可. 1 号分⽚和 2 号分⽚管理的区间都是不变的。这样极大减少了数据搬运的工作量。

优点: ⼤⼤降低了扩容时数据搬运的规模, 提⾼了扩容操作的效率.
缺点: 数据分配不均匀 (有的多有的少, 数据倾斜)

 

(3) 哈希槽分区算法

        最后这一种方法,也是Redis选择的方法。为了解决上述问题 (搬运成本⾼ 和 数据分配不均匀), Redis cluster 引⼊了哈希槽 (hash slots) 算法。

hash_slot = crc16(key) % 16384

        哈希槽总共会被分为个16384个槽位上,每一个key值被转换后,就会被放在这 [0, 16383] 的槽位上。然后再把这些槽位⽐较均匀的分配给每个分⽚. 每个分⽚的节点都需要记录⾃⼰持有哪些分片。例如,我们现如今有三个分片,可以进行如下的分配方式:

0 号分⽚: [0, 5461], 共 5462 个槽位
1 号分⽚: [5462, 10923], 共 5462 个槽位
2 号分⽚: [10924, 16383], 共 5460 个槽位

        这里的分片槽位是很灵活的,Redis内部是通过使用bitmap位图结构区分,这个分片上是否拥有该槽位号,这个位图至少占用占2KB空间(16384(bit)= 2048(byte) = 2 * 1024)。

        当我们新增⼀个 3 号分⽚时,就可以针对原有的槽位进⾏重新分配,⽐如可以把之前每个分⽚持有的槽位, 各拿出⼀点, 分给新分⽚:

⼀种可能的分配⽅式:
0 号分⽚: [0, 4095], 共 4096 个槽位
1 号分⽚: [5462, 9557], 共 4096 个槽位
2 号分⽚: [10924, 15019], 共 4096 个槽位
3 号分⽚: [4096, 5461] + [9558, 10923] + [15019, 16383], 共 4096 个槽位

        在我们实际操作中,不需要去指定哪些槽位要被分配,而是告诉Redis哪些分片能被分配Redis 会⾃动完成后续的槽位分配,以及对应的 key 搬 运的⼯作。当然这是后面的事情。

问题⼀: Redis 集群是最多有 16384 个分⽚吗?        

        当Redis分片被设计为16384时,那么每个分片只能对应一个槽位,要保证每个分片上的数据均匀时很难的。

        当分片包含的槽位够多时,能够直观反映出包含key 的数量。反之,当槽位数量非常少,槽位个数不一定能反应key的个数。实际上 Redis 的作者建议集群分⽚数不应该超过 1000

问题⼆: 为什么是 16384 个槽位?

        在集群中,各个节点通过心跳包通信。这些心跳包中包含了该节点所持有的槽(slots)。16384个槽位,需要2KB大的空间,这个值基本够用。如果设计为65536个,那么就需要8 KB 位图。如果放在计算机存储中看这个大小着实难以起眼,但在通信频繁的网络中,却十分吃网络带宽,会成为通信成本一个不小的开销。

        另外,Redis 集群⼀般不建议超过 1000 个分⽚所以 16k 对于最⼤ 1000 个分⽚来说是⾜够⽤

的, 同时也会使对应的槽位配置位图体积不⾄于很⼤。

Redis集群搭建

        我们仍是基于docker,模拟分布式系统的场景,在低配置的云服务器上完成Redis集群的搭建。

        Redis集群的拓扑图如下:

(1) 创建目录和配置

        创建 redis-cluster ⽬录. 内部创建两个⽂件

谈谈shell脚本

        在Linux系统中,以.sh结尾的文件,统称为“脚本”。我们使用Linux系统时,都是以命令的形式进行操作。这些命令,非常适合写到一个文件之中,进行批量化执行。同时,还能再这些文件之中加入条件、循环、函数等机制,从而能够完成一些复杂的工作。

① 生成Redis节点的配置文件

        generate.sh 内容如下:

for port in $(seq 1 9); \do \mkdir -p redis${port}/touch redis${port}/redis.confcat << EOF > redis${port}/redis.confport 6379bind 0.0.0.0protected-mode noappendonly yescluster-enabled yescluster-config-file nodes.confcluster-node-timeout 5000cluster-announce-ip 172.30.0.10${port}cluster-announce-port 6379cluster-announce-bus-port 16379EOFdonefor port in $(seq 10 11); \do \mkdir -p redis${port}/touch redis${port}/redis.confcat << EOF > redis${port}/redis.confport 6379bind 0.0.0.0protected-mode noappendonly yescluster-enabled yescluster-config-file nodes.confcluster-node-timeout 5000cluster-announce-ip 172.30.0.1${port}cluster-announce-port 6379cluster-announce-bus-port 16379EOF
done

在我们的预期之中,得到11个目录,每一个目录中有一个节点配置文件,每个配置文件中的ip地址是不同的:

bash generate.sh

         通过bash执行脚本后,我们得到如下目录表:

以Redis1/redis.conf为例:

区别在于每个配置中配置的 cluster-announce-ip 是不同的, 其他部分都相同。

👑 配置说明:

•  cluster-enabled yes 开启集群.
•  cluster-config-file nodes.conf 集群节点⽣成的配置
•  cluster-node-timeout 5000 节点失联的超时时间.
•  cluster-announce-port 6379 节点⾃⾝的业务端⼝.
•  cluster-announce-port 6379 节点⾃⾝的业务端⼝.
•  cluster-announce-bus-port 16379 节点⾃⾝的总线端⼝. 集群管理的信息交互 是通过这个端⼝进⾏的.

(2) 编写 docker-compose.yml

        在外面编写了各个节点启动时的配置文件后,到了现在使用docker去创建容器,并在这些容器中启动Redis服务。

① 创建局域网

        docker会为每一个新创建的容器创建新的局域网,各个局域网内部默认是不互通的。所以,在配置文件时,需要首先申请networks,并分配网段,“172.30.0.0/24”,这里分配网段,是为了契合redis.conf文件中写死的静态ip。

② 配置每个节点. 注意配置⽂件映射, 端⼝映射, 以及容器的 ip 地址

        每个容器都是独立的,所以它们可以使用相同的端口号。如果想要通过外部机器,访问容器内的端口,就需要在配置.yml文件中,建立容器端口和外界端口的映射关系。

此处的端⼝映射不配置也可以,。配置的⽬的是为了可以通过宿主机 ip + 映射的端⼝进⾏访问. 通过 器⾃⾝ ip:6379 的⽅式也可以访问。

        我们使用docker compose统一启动容器:

     

(3) 构建集群

        启动容器后,每一个容器上都 运行着redis-server,现在,需要我们使用:

# 创建集群
redis-cli --cluster create  redis_node(ip:port)  --cluster-replicas n--cluster create 表⽰建⽴集群. 后⾯填写每个节点的 ip 和地址
--cluster-replicas n 表⽰每个主节点需要n个从节点备份# 示例:
redis-cli --cluster create 172.30.0.101:6379 172.30.0.102:6379 
172.30.0.103:6379 172.30.0.104:6379 
172.30.0.105:6379 172.30.0.106:6379 172.30.0.107:6379 
172.30.0.108:6379 172.30.0.109:6379 --cluster-replicas 2
# 末尾表示从节点个数

        此处是把前 9 个主机构建成集群, 3 主 6 从. 后 2 个主机暂时不⽤(这两个作为之后扩容的主机待命)。参与构建集群的redis节点都是平等的,所以谁是主节点,谁是从节点不是固定的。但,你总得告诉Redis节点集群的特点(如上述:--cluster-replicas 2)。

    执行完成命令后,我们可以看到如下界面:
       

        如此,集群也就搭建好了。此时, 使⽤客⼾端连上集群中的任何⼀个节点, 都相当于连上了整个集群。

        正因为我们在docker-compose.yml文件中,建立了容器内外端口的映射,所以我们可以是使用两种不同的方式访问redis-server。

# 查看集群节点信息
cluster nodes

        现在,我们是否可以直接存储数据了呢?我们来试试看:

        我们可以直观发现,直接插入这个值是报错的,其原因就在于,key1进行哈希映射后,应该存放的分片是第二个分片,而不是现目前的第一分片内。反观,我们直接插入key2,则能够插入成功,说明key2经过哈希映射后,就是第一分片内。

        那如何解决呢?难道拿着每个分片去试? 那肯定很太呆瓜了。

# 加上-c选项,会⾃动把请求重定向到对应节点
redis-cli -port xxx -c

        我们不仅成功插入了该节点值,访问请求也从第一个分片,重定向到了第二分片!

        所以,在集群之后,Redis之前的有一些命令就无法正常使用。例如,获取多个key就无法正常执行,因为key都被分配到了不同的分片上。


Redis集群容灾、故障转移

        当集群中有节点挂了。如果是从节点,那还好说,因为它不承担写操作。可一旦是主节点挂了呢?一旦客户端发送的全都是写请求,无一例外都会失败。

        我们现在⼿动停⽌⼀个 master 节点, 观察其效果:

# 停止容器
docker stop redis1

        

        我们能够看到,在redis1挂掉之后,集群做的工作就同哨兵类似,会在当前片中选择出从节点,提拔为主节点。

        集群机制的处理,也叫做故障转移

故障转移:

        集群机制中的故障转移,同哨兵机制中这块的处理流程还是有一些细微不同的。

① 故障判断

        集群中的所有节点, 都会周期性的使⽤⼼跳包进⾏通信。

🥎节点 A 给 节点 B 发送 ping 包, B 就会给 A 返回⼀个 pong 包. ping 和 pong 除了 message type 属性之外, 其他部分都是⼀样的. 这⾥包含了集群的配置信息。

🥎每个节点, 每秒钟, 都会给⼀些随机的节点发起 ping 包, ⽽不是全发⼀遍。

🥎当节点 A 给节点 B 发起 ping 包, B 不能如期回应的时候, 此时 A 就会尝试重置和 B 的 tcp 连接, 看能 否连接成功. 如果仍然连接失败, A 就会把 B 设为 PFAIL 状态。

🥎A 判定 B 为 PFAIL 之后, 会通过 redis 内置的 Gossip 协议, 和其他节点进⾏沟通, 向其他节点确认 B 的状态。(每个节点都会维护⼀个⾃⼰的 "下线列表“)。

🥎此时 A 发现其他很多节点, 也认为 B 为 PFAIL, 并且数⽬超过总集群个数的⼀半, 那么 A 就会把 B 标 记成 FAIL (相当于客观下线), 并且把这个消息同步给其他节点。

        这也就是为什么我们使用cluster nodes时,看到redis1 的”FAIL“字样。

以下三种情况会出现集群宕机:
某个分⽚, 所有的主节点和从节点都挂了.
某个分⽚, 主节点挂了, 但是没有从节点.
超过半数的 master 节点都挂了.

② 故障迁移

        如果检测到故障的节点不是主节点,那么就不会执行故障迁移。如果故障节点是主节点就会,触发故障迁移了。所谓故障迁移, 就是指把从节点提拔成主节点, 继续给整个 redis 集群提供⽀持.

具体流程如下:

🏀 从节点判定⾃⼰是否具有参选资格。如果长时间都未与主节点通信过,那么从节点上的数据太过陈旧。

🏀具有资格的节点, ⽐如 C 和 D, 就会先休眠⼀定时间. 休眠时间 = 500ms 基础时间 + [0, 500ms] 随机时间 + 排名 * 1000ms. offset 的值越⼤, 则排名越靠前(越⼩)。 选取节点的过程不重要,重要的是选出节点作为主节点。

🏀 当C的休眠时间先到, C 就会给其他所有集群中的节点, 进⾏拉票操作。

🏀主节点就会把⾃⼰的票投给 C,当 C 收到的票数超过主节点数⽬的⼀半, C 就会晋升成主节点。(从节点是不会参与投票的)。

🏀同时, C 还会把⾃⼰成为主节点的消息, 同步给其他集群的节点. ⼤家也都会更新⾃⼰保存的集群结构信息.        
        上述选举的过程, 就是Raft 算法, 是⼀种在分布式系统中⼴泛使⽤的算法。在随机休眠时间的加持下, 基本上就是谁先唤醒, 谁就能竞选成功。
所以,同哨兵机制的选举流程相比,故障迁移除去了 先选leader 再挑选主节点的过程,而是直接投票选出主节点。
回到实验中来,当我们再次重启原来“挂了”的redis1时,redis1会直接变为从节点,正常运行。

Redis集群扩容

        扩容实在实际开发中比较常见的场景。随着业务的发展, 现有集群很可能⽆法容纳⽇益增⻓的数据. 此时给集群中加⼊更多新的机器, 就可以使 存储的空间更⼤了。

(1) 把新节点加⼊到集群        

        上⾯已经把 redis1 - redis9 重新构成了集群. 接下来把 redis10 和 redis11 也加⼊集群。

#  add-node 后的第⼀组地址是新节点的地址. 第⼆组地址是集群中的任意节点地址 表示要加入到的集群
redis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:6379

               

(2) 重新分配 slots

        上图能够很清晰地看到,我们新加的节点110是成为了主节点,但没有被分配任何slots。

# 重新分配 reshard 后的地址是集群中的任意节点地址
redis-cli --cluster reshard 172.30.0.101:6379

           执行后,首先提示我们需要多少个slots。

        填入新的id:       

          一般情况下,是选择第一种方法。

        搬运过程:

        确定之后, 会初步打印出搬运⽅案, 让⽤⼾确认. 之后就会进⾏集群的 key 搬运⼯作. 这个过程涉及到数据搬运. 可能需要消耗⼀定的时间.

(3) 给新的主节点添加从节点

        光有主节点此时扩容的⽬标已经初步达成但是为了保证集群可⽤性,我们之前还创建了一个redis11,现在就是要将这个节点加入到才创建的主节点之中。

# 后面需要携带 作为从节点加入
redis-cli --cluster add-node 172.30.0.111:6379 172.30.0.101:6379 --cluster-slave
        执⾏完毕后, 从节点就已经被添加完成了,我们也可以查询得到。

        

Redis集群缩容

        扩容是⽐较常⻅的, 但是缩容其实⾮常少⻅. 此处我们简单了解缩容的操作步骤即可。

        接下来演⽰把 110 111 这两个节点删除。

(1) 删除从节点

# redis-cli --cluster del-node [集群中任⼀节点ip:port] [要删除的从机节点 nodeId]
redis-cli --cluster del-node 172.30.0.101:6379 09cc4432e6a407c341186748ea4d5acd691d24a7

(2) 重新分配 slots        

redis-cli --cluster reshard 172.30.0.101:6379

        执⾏后仍然进⼊交互式操作。此时要删除的主节点, 包含 4096 个 slots。这个注解上的这 4096 个 slots 分成三份 (1365 + 1365 + 1366), 分别分给其他三个主节点。这样可以使 reshard 之后的集群各个分⽚ slots 数⽬仍然均匀。

例:分配给 102 1365 个 slots

        照此方式,我们将剩余的slot,依次分配给其他的主节点。

(3) 删除主节点

        110 节点从集群中删除。

# redis-cli --cluster del-node [集群中任⼀节点ip:port] [要删除的从机节点 nodeId]
redis-cli --cluster del-node 172.30.0.101:6379 ac67fc8a7e1e02a3e1d231dd64db9789f4d02879

        ⾄此, 缩容操作完成.


总结:

        本篇我们谈论了如下的几个问题:
① 集群是什么?解决了说明问题?

② 数据分片算法:哈希求余、一致性哈希算法、哈希槽算法

③ 搭建redis集群

④ 集群容灾、故障转移

⑤ 集群扩容、缩容


本篇到此结束,感谢你的阅读。

祝你好运,向阳而生~

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

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

相关文章

【算法小记】——机器学习中的概率论和线性代数,附线性回归matlab例程

内容包含笔者个人理解&#xff0c;如果错误欢迎评论私信告诉我 线性回归matlab部分参考了up主DR_CAN博士的课程 机器学习与概率论 在回归拟合数据时&#xff0c;根据拟合对象&#xff0c;可以把分类问题视为一种简答的逻辑回归。在逻辑回归中算法不去拟合一段数据而是判断输入…

git使用指南——以gitlab为例

注册gitlab 自行注册 新建项目 选择新建一个空白的项目 上传项目 clone项目地址到本地 执行完之后&#xff0c;会在目录下生成如下内容&#xff1a;进入里面&#xff0c;选择.git&#xff0c;要上传的内容&#xff08;资料或代码复制到该目录下&#xff09;&#xff1a;…

【MATLAB源码-第123期】基于matlab的SSK(空间位移键控)调制和QSSK(正交空间位移键控)调制误码率对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 1. SSK&#xff08;空间位移键控&#xff09;&#xff1a; - 基本原理&#xff1a;SSK是一种MIMO&#xff08;多输入多输出&#xff09;系统中的调制技术。它通过选择性地激活不同的发送天线来传输信息。在每个时间槽&…

Nuxt2.x Error页面返回自定义请求状态码

一、问题描述 最近接到一个需求&#xff0c;针对Nuxt2.x的一个项目进行SEO优化&#xff0c;需要对404页面的状态进行修改&#xff0c;将404页面的请求状态码改为301&#xff0c;而不是404&#xff1a; 二、解决方案 1.几种无效尝试 &#xff08;1&#xff09;layouts下的err…

多家头部企业宣布「启动鸿蒙原生应用开发」,你看好鸿蒙系统走向「独立」吗?

我觉得跟着国家对鸿蒙的推进&#xff0c;就知道发展前景肯定是没错的。并且现在已经走向独立道路。 华为鸿蒙是国家主推的操作系统项目&#xff0c;而之前由于美丽国的一系列制裁后。华为也终于崛起&#xff1b;在1月18号的华为鸿蒙仪式中&#xff0c;推出了HarmonyOSNEXT星空…

Java基础 - 09 Set之linkedHashSet , CopyOnWriteArraySet

LinkedHashSet和CopyOnWriteArraySet都是Java集合框架提供的特殊集合类&#xff0c;他们在特定场景下有不同的用途和特点。 LinkedHashSet是Java集合框架中的一种实现类&#xff0c;它继承自HashSet并且保持插入顺序。它使用哈希表来存储元素&#xff0c;并使用链表来维护插入…

Git学习笔记(第3章):Git常用命令

目录 3.1 设置用户签名 3.2 初始化本地库 3.3 查看本地库状态 3.4 添加暂存区 3.5 提交本地库 3.6 历史版本 3.7 修改文件 3.8 版本穿梭 小结 命令 作用 git config --global user.name 用户名 设置用户签名 git config --global user.email 邮箱 设置用户签名 …

全国各城市绿地及绿化面积数据,shp/excel格式,2020-2022年

基本信息. 数据名称: 全国各城市绿地及绿化面积数据 数据格式: Shp、excel 数据时间: 2020-2022年 数据几何类型: 面 数据坐标系: WGS84 数据来源&#xff1a;网络公开数据 数据字段&#xff1a; 序号字段名称字段说明1province省份名称2city城市名称4city_dm城市…

【python】python实现代码雨【附源码】

欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 一、效果图&#xff1a; 二、准备工作 &#xff08;1)、导入必要的模块&#xff1a; 代码首先导入了需要使用的模块&#xff1a;requests、lxml和csv。 import requests from lxml import etree import csv 如果出现模…

Linux大老都是怎么记住这么多命令的?

今天给大家带来的是面试/实际工作中经常用到的Linux相关操作命令: 一. vi/vim编辑器 ---->文本编辑器 作用&#xff1a;创建文件&#xff0c;编辑文件&#xff0c;查看文件 格式&#xff1a;vi/vim 文件的名字 解析&#xff1a;如果该文件不存在&#xff0c;vi就会创建该…

外包干了一个月,技术退步明显。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入南京某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

【QML COOK】- 012-在QML中使用OpenGL渲染

本文是参照了两个文章编写的。 一是QML的例子&#xff1a;Scene Graph - OpenGL Under QML | Qt Quick 6.6.1 二是关于SceneGraph的介绍&#xff1a;Qt Quick Scene Graph | Qt Quick 6.2.11 这里我提取一下重点方便大家理解。 一个QML程序只能用一种计算机图形接口。不能有…

基于SpringBoot Vue美食网站系统

大家好✌&#xff01;我是Dwzun。很高兴你能来阅读我&#xff0c;我会陆续更新Java后端、前端、数据库、项目案例等相关知识点总结&#xff0c;还为大家分享优质的实战项目&#xff0c;本人在Java项目开发领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#x…

测试工程师必看!测试用例设计全解析,让你彻底掌握

测试工程师在入行时&#xff0c;都会接触到一个名词——测试用例&#xff0c;都知道测试用例是干什么用的&#xff0c;提到设计测试用例的方法&#xff0c;大部分测试工程师都会侃侃而谈&#xff1a;等价类法、边界值法、判定表法、正交分解法……这些方法说起来都如数家珍&…

揭秘大数据时代的内存数据存储、数据缓存:redis缓存框架!

介绍&#xff1a;Redis 是一个开源的、基于内存的数据存储系统&#xff0c;它也可以持久化数据到硬盘上。Redis 以其高性能、高可靠性和丰富的数据结构支持而著称&#xff0c;在现代应用程序中被广泛用作数据库、缓存和消息中间件。 主要特点&#xff1a; 键值存储&#xff1a;…

Unity工程没有创建.sln文件,导致打开C#文件无法打开解决方案

最近又开始折腾些Unity的小项目&#xff0c;重新遇到一些常见的小问题 点击报错文件 却没有打开文件 于是查看了下打开Window->Package Manager 选择Unity Registry 搜索Visual Studio Editor&#xff0c;发现并没有安装 同理&#xff0c;也可以安装VSCode的插件 问题解决了…

.NET集成IdGenerator生成分布式全局唯一ID

前言 生成分布式唯一ID的方式有很多种如常见的有UUID、Snowflake&#xff08;雪花算法&#xff09;、数据库自增ID、Redis等等&#xff0c;今天我们来讲讲.NET集成IdGenerator生成分布式全局唯一ID。 分布式ID是什么&#xff1f; 分布式ID是一种在分布式系统中生成唯一标识符…

实用的SQLite数据库可视化管理工具推荐

前言 俗话说得好“工欲善其事&#xff0c;必先利其器”&#xff0c;合理的选择和使用可视化的管理工具可以降低技术入门和使用门槛。今天推荐7款实用的SQLite数据库可视化管理工具(GUI)&#xff0c;帮助大家更好的管理SQLite数据库。 什么是SQLite&#xff1f; SQLite是一个…

【每日一题】2.LeetCode——删除有序数组中的重复项

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&…

JavaScript中的事件

&#xff11; 事件&#xff08;Event&#xff09; 事件也就是用户或者浏览器执行的某种动作&#xff0c;而JS与Html之间的交互是通过事件而来的。使用仅在事件发生时执行的**监听器&#xff08;事件处理程序&#xff09;**来订阅事件。web浏览器可以发生多种事件&#xff0c;在…