Redis(三)分布式应用

2023-11-17

一、分布式支持

1、性能

Redis本身的QPS已经很高了,但是如果在一些并发量非常高的情况下,性能还是会受到影响。这个时候我们希望有更多的Redis服务来分摊压力,实现负载均衡。

2、高可用

如果只有一个Redis服务,一旦服务宕机,那么所有的客户端都无法访问,会对业务造成很大的影响。另一个,如果硬件发生故障,而单机的数据无法恢复的话,带来的影响也是灾难性的。

3、可扩展

因为Redis所有的数据都放在内存中,如果数据量大,很容易受到硬件的限制。升级硬件收效和成本比太低,所以我们需要有一种横向扩展的方法。

高性能、高可用、扩展性需要依赖两种关键的技术,一种是分片,一种是冗余。分片的意思是把所有的数据拆分到多个节点分散存储。冗余的意思是每个节点都有一个或者多个副本。那么,Redis必须要提供数据分片和主从复制的功能。副本有不同的角色,如果主节点发生故障,则把某个从节点改成主节点,访问新的主节点,实现高可用。

二、主从复制

1、主从配置

一)配置文件方式

在slave节点里添加master节点的IP即可配置成功

replicaof 127.0.0.1 6379

slave节点启动后,会自动连接到master节点
如果master节点改变,比如master节点宕机,选举出新的master,这个配置会被重写。

二)启动服务方式

可以在服务启动时,通过参数直接指定master节点。

./redis-server --slaveof 127.0.0.1 6379

三)运行中方式

一个正在运行中的节点可以通过命令变为master节点的slave节点

slaveof 127.0.0.1 6379

四)级联复制

一个从节点也可以是其他节点的主节点,形成级联复制的关系。
参看集群状态

redis> info replication

从节点是只读的,不能执行写操作。
在master节点写入之后,slave节点会自动从master节点同步数据。

五)去除主从关系

如果想要解除主从关系,可以直接去掉配置文件中的replicaof语句后重启,或者直接执行命令

redis> slaveof no one

此时从节点会变为自己的主节点,结束复制数据。

2、主从复制原理

Redis的主从复制分为两种,第一种是全量复制,即一个节点首次连接到master节点后,需要复制全部数据。第二种是增量复制,即已经连接到master节点的slave节点忽然断线或宕机时,缺失部分数据,需要同步过来。

一)全量复制

1)连接阶段

  1. slave节点启动时(或执行slaveof命令时),会在自己本地保存master节点的信息,包括master节点的host和ip
  2. slave节点内部有个定时任务,replicationCron,每隔一秒检查是否有新的master node要连接和复制。如果发现有master节点,就和master节点建立连接,如果连接成功,slave节点就为链接分配一个专门处理复制工作的文件事件处理器负责后续的复制工作。为了让master节点感知到slave节点的存活,slave节点定时给主节点发送ping请求。

2)数据同步阶段

  1. 如果是新加入的节点,那就需要全量复制。master通过bgsave命令在本地生成一份RDB快照。
  2. RDB快照生成后,会发送给slave节点。如果slave本来就有数据,则会清除旧数据,然后再使用RDB快照进行加载。
  3. master节点生成RDB文件时,master节点会把所有新的写命令缓存在内存中。在slave节点保存了RDB之后,再将新的写命令赋值给slave节点。这里的处理思路和AOF日志在进行日志文件压缩时的处理思路是一样的,先缓存命令,新文件生成后进行命令的同步。

3)命令传播阶段

master节点持续把写命令异步复给slave节点

4)总结

master节点先用RDB快照文件将数据传递给slave节点,然后将写命令发送给slave节点。
但在一般情况下,Redis不需要做读写分离,因为其吞吐量已经足够大,做成集群分片之后并发问题更少,所以不需要考虑主从延迟问题,但主从延迟依旧是不可避免地,只能通过提升网络性能改善。

二)增量复制

如果slave节点有一段时间断开了与master节点的连接,如果不想清空数据重新记录,就需要记录上次复制的偏移量。
slave通过master_repl_offest记录复制偏移量,获取偏移量命令:

redis> info replication

三)无盘复制

Redis 6.0的新特性,主从复制的无盘复制。
为了降低master节点的磁盘开销,Redis支持无盘复制,master节点生成的RDB文件不再保存到磁盘,它将直接被发送到slave节点。无盘复制适用于主节点磁盘性能紧张但网络性能宽裕的境况。

3、主从复制问题

Redis主从复制解决了数据备份容灾和部分性能问题,但没有解决高可用问题,如果master节点宕机,对外服务就会断掉,如果每次都手动更换节点配置就过于麻烦。

三、可用性保证(Sentinel哨兵机制)

高可用的前提是既要能实现主从切换,也要能通知客户端发生了主从切换,在RocketMQ中,NameServer为它提供了主从的切换帮助,我们可以借用它的部分思路来参考。
创建一个监控器来监控全部的Redis节点状态,如果某一个节点超过一段时间内没有给监控器发送心跳报文,就标记该节点离线,如果该节点是master节点,就把某一个slave节点变成master节点,应用每次都从监控器中取得master节点地址。
在Redis中,它实现了一个类似机制来进行对应控制,Sentinel(哨兵)

1、Sentinel哨兵原理

通常而言,Sentinel服务会有奇数个(src/redis-sentinel),可以通过sentinel的脚本启动,也可以用redis-server的脚本加入sentinel参数启动

./redis-sentinel../sentinel.conf

./redis-sentinel../sentinel.conf --sentinel

Sentinel的本质是一个运行在特殊模式下的Redis,Sentinel通过info命令得到被监听Redis机器的master,slave等信息
在这里插入图片描述
为了保证监控服务器的可用性,我们会对Sentinel做集群部署,Sentinel既监控所有的Redis节点,也互相监控,它们没有主从之分。
Sentinel是特殊的Redis节点,它本身具有发布订阅功能,哨兵上线时会给所有的Redis节点的名字为_sentinel_:hello的channel发送消息,每个哨兵都订阅了所有Redis节点名字为_sentinel_:hello的channel,所以哨兵之间可以互相监控,互相感知。

2、Sentinel哨兵监控下线

一)主观下线

Sentinel默认以每秒钟1次的频率向Redis服务节点发送PING命令,如果在指定时间内没有收到有效回复,Sentinel就会将该节点标记为下线。
但是这个下线并非是真正的节点下线,所以称为主观下线。
控制参数为:sentinel down-after-milliseconds
默认为30秒

二)客观下线

当一个节点主观下线,并不一定真的下线了,可能是网络出现波动,导致没有收到消息,所以这时第一个发现该节点下线的Sentinel节点会继续询问其他的Sentinel节点,确认这个节点是否下线,如果多数Sentinel都认为该节点已经下线,那么该节点就被认定为真正下线(客观下线)
如果客观下线的是master节点,就需要重新选举master节点。

3、Sentinel哨兵故障转移

Redis的选举与故障转移都交给Sentinel来进行,因此选举的第一步,是在Sentinel中选举出一个leader,然后由leader完成故障转移流程,Sentinel通过Raft算法进行选举。

一)Sentinel哨兵的Leader选举

1)传统Raft算法

Raft算法是一种共识算法,其核心思想是:先到先得,少数服从多数。

  1. 分布式环境中的节点有三种状态:follower,candidate,leader
  2. 一开始所有的节点都是follower状态,如果follower连接不到leader,它就会成为candidate。candidate请求其他节点的投票,其他的节点会投给它。如果它得到了大多数节点的投票,他就成为了主节点,这个过程就叫Leader Election
  3. 现在所有的写操作需要在leader节点上发生,leader会记录操作日志,没有同步到其他follower节点的日志,状态为uncommitted。等到超过半数的follower同步了这条记录,日志状态就会变成committed。leader会透支所有的follower日志已经committed,这个时候所有的节点就达成了一致。这个过程叫Log Replication
  4. 在Raft协议里,选举有两个超时时间。第一个叫election timeout,目的是为了放置同一时间大量节点参与选举,没一个节点在变成candidate之前需要随机等待一段时间,时间范围是150ms至300ms。第一个变成candidate的节点会先发起投票,它第一票投给自己,然后请求其他节点投票
  5. 如果还没有收到投票结果,又到了超时时间,需要重置超时时间。只要有大部分节点投给了一个节点,它就会成为leader。
  6. 成为leader之后,它会发消息让其他节点来同步数据,发消息的间隔是由heartbeat timeout来控制的。follower会回复同步数据的消息。
  7. 只要followers收到了同步数据的消息,代表leader没有挂掉,它们会清除heartbeat timeout的计时。
  8. 一旦followers在heartbeat timeout时间之内没有收到Append Rnteries消息,它就会认为leader挂了,开始让其他节点投票,成为新的leader。
  9. 必须有超过半数以上的节点投票,保证只有一个leader被选出来。
  10. 如果两个follower同时变成了candidate,就会出现分割投票,比如有两个节点同时变成candidate,而且各自有一个投票请求先达到了其他节点 ,加上它们给自己的投票,每个candidate手上有两票,但是,因为它们的election timeout不同,再发起新的一轮选举的时候,有一个节点获得了更多的投票,它被选举成leader。

2)Sentinel的Raft算法

Sentinel的选举和传统Raft算法略有不同

  1. master客观下线触发选举,而不是过了election timeout时间开始选举
  2. Leader不会自己成为Leader的消息发送给其他Sentinel,其他Sentinel等待Leader从Slave选举出Master后,检测到新的Master正常工作,就会去掉客观下线的标识,不去进去故障转移流程。

二)Master节点的故障转移

Redis的master节点选举规则有四条,分别是断开连接时长,优先级排序,复制数量和进程ID

  1. 如果与哨兵连接断开时间太久,超过了某个阈值,就直接失去选举权
  2. 如果拥有选举权,就会比较优先级,这个在配置文件中可以进行设置(replica-priority 100),数值越小优先级越高,默认为100
  3. 如果优先级相同,就看谁从master中复制的数据做多(复制偏移量最大),选最多的那个
  4. 如果复制偏移量相同,就选进程ID最小的那个。

三)Follower节点的生成

Redis选举出新的Master节点后,其他的节点会成为它的Follower节点,步骤分为两步。

  1. 选出Sentinel Leader之后,有Sectinel Leader向某个节点发送slaveof no one命令,让它成为独立节点
  2. 向其他节点发送slaveof x.x.x.x xxxx(Maser节点的ip与端口号),让其他节点成为Master节点的从节点,故障转移完成

3、Sentinel哨兵的功能总结

  • 监控:Sentinel会不断检查主服务器和从服务器是否正常运行
  • 通知:如果某一个被监控的节点出现问题,Sentinel会通过API发送通知
  • 自动故障转移:如果主服务器发生故障,Sentinel可以启动故障转移过程,把某台从服务器升级为主服务器,并发送通知
  • 配置管理:客户端连接到Sentinel,获取当前的Redis主服务器地址。

4、Sentinel哨兵的不足

  1. 主从切换的时候会丢失部分数据,因为只有一个Master节点
  2. 只能单点写入,没有水平扩容
  3. 如果数据量非常大,这个时候就要进行Redis数据分片,需要多个master-slave的group,把数据分布到不同的group中。

四、分布式方案

关于Redis数据分片,有三种可能方案

  1. 客户端实现相关逻辑,例如用取模或者一致性哈希对key进行分片,查询和修改都先判断key的路由
  2. 把分片处理的逻辑抽取出来,运行一个独立的代理服务,客户端连接到这个代理服务,代理服务做请求的转发。
  3. 服务端实现

1、客户端实现

在Jedis客户端中,支持分片功能,它是Spring Boot 2.x版本之前默认的Redis客户端,RedisTemplate就是对Jedis的封装

一)ShardedJedis

Jedis有几种连接池,其中一种支持分片。
例如:

Public class ShardedTest {
	public static void mian (String[] args){
		JedisPoolConfig poolConfig = new JedisPoolConfig();
		//Redis服务器
		JedisShardInfo shardInfo1 = new JedisShardInfo("127.0.0.1",6379);
		JedisShardInfo shardInfo2 = new JedisShardInfo("127.0.0.2",6379);
		//连接池
		List<JedisShardInfo> infoList = Arrays.asList(shardInfo1,shardInfo2);
		ShardedJedisPool jedisPool = new ShardedJedisPool(poolConfig,infoList);
		ShardedJedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			for(int i=0;i<100;i++)[
				jedis.set("Key"+i,""+i);
			}
			for(int i=0;i<100;i++)[
				jedis.get("Key"+i);
			}
		}finally{
			if(jedis != null){
				jedis.close();
			}	
		}
	}
}

执行后,会发现两个Redis的key数量是非常接近的。
如果希望是数据分布相对均匀的话,首先可以考虑哈希后取模(因为Key不一定是整数,所以需要先计算哈希)

1、哈希后取模

例如,hash(key)%N,根据余数,,决定映射到哪一个节点。这种方式比较简单,属于静态的分片规则。但是一旦节点数量发生变化,由于N的数值发生变化,数据需要重新分布。
为了解决这个问题,可以采用一致性哈希算法

2、一致性哈希

把所有的哈希值空间组成一个虚拟圆环(哈希环),整个空间按顺时针方向组织,因为是环形空间,0和2^32-1是重叠的。
假设有四台机器需要哈希环来实现映射(分布数据),现根据机器命名或IP地址计算哈希值,然后分布到哈希环中。
现在有4条数据或者4个访问请求,针对key计算后,得到哈希环中的位置,沿着哈希环顺时针找到的第一个Node,就是数据存储的节点。
在这种情况下,新增加一个节点,只影响一部分数据的分布,减少一个节点,只影响相邻的下一个节点。
一致性哈希解决了动态增删节点时,所有数据都需要重新分布的问题,它只会影响到下一个相邻的节点,对其它节点没影响。
但是这样的一致性哈希算法有一个缺点,因为节点不一定是均匀地分布的,特别是在节点数比较小的情况下,所以数据不能得到均匀分布。解决这个问题的办法是引入虚拟节点(Virtual Node),一个实际节点设置数个虚拟节点后,按照原来的方式继续顺时针存储,虚拟节点存储的数据会存储在实际节点上,节点数提高后,数据分布会更加均匀。
一致性哈希在分布式系统,负载均衡,分库分表等场景中都有体现,是基础算法。

3、红黑树

在Jedis中,通过红黑树结构实现一致性哈希算法。

private void initialize(List<S> shards) {
	//创建一个红黑树
	nodes = newTreeMap<Long, S>();
	//把所有的Redis节点放到红黑树中
	for(int i=0;i!=shards.size();++i){
		final S shardInfo = shards.get(i);
		//为每一个Redis节点创建160个虚拟节点,放到红黑树中
		if(shardInfo.getName()==null)for(int n=0;n<160*shardInfo.getWeight();n++){
			nodes.put(this.algo.hash("SHARD-"+i+"-NODE-"+n),shardInfo);
		}
		else for(int n=0;n<160*shardInfo.getWeight();n++){
			//对名字计算哈希(MurmurHash),名称格式SHARD-0-NODE-0
			nodes.put(this.algo.hash(shardInfo.getName+"*"+shardInfo.getWeight()+n),shardInfo);
		}
		//添加到map中,键为ShardInfo,置为redis实例
		resources.put(shardInfo,shardInfo.createResource());
	}
}

当存取键值对时,计算键的哈希值,然后从红黑树上摘下比该值大的第一个节点上的JedisShardInfo,此即为需要存储的虚拟节点,随后从Resources取出Jedis。

public R getShard(String key) {
	return resources.get(getShardInfo(key));
}

根据虚拟节点,取红黑树子集,找出比它大的第一个节点,此为实际节点,如果子集为空,那么它自身就是实际节点。

public S getShardInfo(byte[] key) {
	//获取比当前key的哈希值更大的红黑树的子集
	SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key));
	if(tail.isEmpty()) {
		//没有比它更大的了,直接从nodes中取出
		return nodes.get(nodes.firstKey());
	}
	//返回第一个比它大的JedisShardInfo
	return tail.get(tail.firstKey());
}

4、总结

使用ShardedJedis之类的客户端分片代码的优势是配质简单,不依赖于其他中间件,分区的逻辑可以自定义,比较灵活,。但是基于客户端的方案,不能实现动态的服务增减,每个客户端需要自行维护分片策略,存在重复代码。

2、Redis Cluster

Redis Cluster是在Redis 3.0版本推出的,用来解决分布式需求,同时可以实现高可用,采用去中心化架构,客户端可以连接到任意一个可用节点。
数据分片有三个关键问题需要解决:

  1. 数据怎么相对均匀地分片
  2. 客户端怎么访问到相应的节点和数据
  3. 重新分片的过程怎么保证正常服务

一)架构

Redis Cluster可以看成是由多个Redis实例组成的数据集合,客户端不需要关注数据的子集到底存储在哪个节点,只需要关注这个集合整体。
在这里插入图片描述

二)数据分布

1、虚拟插槽

Redis既没有用哈希取模,也没有用一致性哈希,而是用虚拟槽实现的。
Redis创建了16384个槽(slot),每个节点负责一定区间的slot。比如Node1负责0-3000,Node2负责3001-6000,Node3负责6001-9000……以此类推。
对象分布到Redis节点上时,对Key用CRC16算法再取模16384,得到一个slot的值,数据落到负责这个slot的Redis的节点上。
Redis的每个master节点都会维护自己负责的slot,用一个bit的序列实现,比如:序列的第0位是1,就代表第一个slot是它负责,序列的第1位是0,就代表第二个slot不归它负责。
查看key属于哪个slot的命令:

redis> cluster keyslot key

key和slot的关系是永远不变的,会变的只有slot和Redis节点的关系。

2、同节点计算

有些操作是不能跨越节点的,比如同一用户的基本信息和其他信息,最好是放置在同一个节点上。当有这种需要时,只需要在key的值里加入{hash tag}即可。Redis在计算槽编号的时候只会获取{}大括号之间的字符串进行槽编号计算,这样就算上面两种信息key不同,但只要大括号内的字符串相同,就可以计算出相同的槽,最后存储到同一节点。

3、客户端重定向

当客户端请求一个数据时,有可能请求的数据并不在当前节点,这时服务端就会返回MOVED,这个结果是根据key计算出来的slot不归当前请求节点,服务端返回MOVED告诉客户端应该去访问哪一个节点来获取数据,此时只有更换节点,才能获取到想要的数据。
这样的话。就需要连接两次,因此Jedis等客户端就会在本地维护一份slot——node的映射关系,大部分时候不需要重定向,所以叫smart jedis(需要客户端支持)

4、数据迁移

因为key和slot的关系永远不变,当新增了节点的时候,需要吧原有的slot分配给新的节点负责,并把相关数据迁移过来。
新增节点(新节点127.0.0.1:7124):

redis-cli --cluster add-node 127.0.0.1:7123 127.0.0.1:7124

新增的节点没有哈希槽,不能分布数据,因此在原来的任意一个节点上执行重分配命令:

redis-cli --cluster reshard 127.0.0.1:7123 

Redis会自动进行数据迁移,将槽分配给新的节点。
当然也可以通过手动输入需要分配的哈希槽的数量和哈希槽的来源节点(all或者指定id)
一些槽命令:

  1. cluster addslots [slot……]:将一个或多个槽指派给当前节点
  2. cluster delslots [slot……]:移除一个或多个槽对当前节点的指派
  3. cluster flushslots :移除指派给当前节点的所有槽,让当前节点变成没有指派任何槽的节点。
  4. cluster setslot node <node_id> :将槽solt指派给node_id指定的节点,,如果槽已经指派给另一个节点,那么就让另一个节点删除该槽,再进行指派。
  5. cluster setslot migrating <node_id> :将本节点的槽slot迁移到node_id指定的节点中
  6. cluster setslot importing <node_id> :从node_id指定的节点中导入槽到本节点
  7. cluster setslot stable :取消对槽的导入(importing),或迁移(migrating)

5、高可用和主从切换原理

当slave发现自己的master变为Fail状态后,便会尝试进行Failover,希望成为新的master。由于宕机的master可能会有多个slave。从而引发选举,其过程如下:

  1. slave发觉自己的master变为Fail状态
  2. 将自己记录的集群currentEpoch加1,并广播FAILOVER_AUTH_REQUEST信息
  3. 其他节点收到该消息,只有master响应,判断请求者合法性,并发送FAILOVER_AUTH_ACK,对每一个epoch只发送一次ACK
  4. 尝试failover的slave收集FAILOVER_AUTH_ACK
  5. 超过半数可成为master
  6. 广播pong通知其他集群节点

Redis Cluster 技能实现主从的角色分配,也能实现主从切换,相当于集成Replication和Sentinel的功能。

三)总结

Redis Cluster的特点:

  1. 无中心结构
  2. 数据按照slot存储分布在多个节点,节点间数据共享,可动态调整数据分布。
  3. 可扩展性,线性扩展可到1000节点,节点可动态添加或删除,
  4. 高可用性,部分节点不可用时,集群仍可用,通过增加slave做standby数据副本,能够实现故障自动转移,节点之间通过gossip协议交换消息,用投票机制完成slave到master的角色转移。
  5. 降低运维成本,提高系统的可扩展性和可用性。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redis(三)分布式应用 的相关文章

随机推荐

  • Python之数据分析(三维立体图像、极坐标系、半对数坐标)

    文章目录 写在前面 一 三维立体图像 1 三维线框 2 三维曲面 3 三维散点 二 极坐标系 三 半对数坐标 写在前面 import numpy as np import matplotlib pylab as mp 因此文章中的np就代表
  • Linux学习大纲

  • 以太网设计FAQ:以太网MAC和PHY

    问 如何实现单片 以太网 微控制器 答 诀窍是将微控制器 以太网媒体接入控制器 MAC 和物理接口收发器 PHY 整合进同一芯片 这样能去掉许多外接元器件 这种方案可使MAC和PHY实现很好的匹配 同时还可减小引脚数 缩小芯片面积 单片以太
  • MySQL第一讲 一遍让你彻底掌握MVCC多版本并发控制机制原理

    Mysql在可重复读隔离级别下 同样的sql查询语句在一个事务里多次执行查询结果相同 就算其它事务对数据有修改也不会影响当前事务sql语句的查询结果 这个隔离性就是靠MVCC Multi Version Concurrency Contro
  • Python爬虫入门8:BeautifulSoup获取html标签相关属性

    前往老猿Python博客 https blog csdn net LaoYuanPython 一 引言 在上节 https blog csdn net LaoYuanPython article details 113091721 Pyth
  • 【神经网络搜索】DARTS: Differentiable Architecture Search

    GiantPandaCV DARTS将离散的搜索空间松弛 从而可以用梯度的方式进行优化 从而求解神经网络搜索问题 本文首发于GiantPandaCV 未经允许 不得转载 1 简介 此论文之前的NAS大部分都是使用强化学习或者进化算法等在离散
  • io回顾

    package test import java io 读取一个文件 并打印在控制台上 class FileReaderTest public static void main String args throws IOException
  • Golang 项目部署实战

    一直认为不懂部署的开发工程师不是好的开发工程师 以下以一些实例讲解自己在项目中的 Golang 后端部署的情况 一般部署脚本应该具有构建 启动 停止 回滚已经查看记录日志等功能 以下分别将这些功能以单个脚本的形式给出 当然也可以写成Make
  • 数学建模算法体系分类

    数学建模算法体系 数据预处理模型 插值拟合 插值 拉格朗日插值法 分段线性插值 牛顿插值 埃尔米特 Hermite 插值 三次样条插值 n维数据插值 拟合 最小二乘法 评价 拟合工具箱 主成分分析 聚类分析 均值 方差分析 协方差分析等统计
  • 基于STM32的智能泊车系统

    一 前言 想起第一次参加的电子设计校赛好像在昨天一样 虽然确实在昨天 但是为了纪念这一段时间的劳动 并且记录一下遇到的问题 所以写了这篇文章 二 实现思路 通过手机向停车场发送停车请求 然后停车场通过判断车位是否为空 控制继电器打开相应的电
  • asp.net zero 8.2 学习-4-创建接口及服务

    上一节 在框架添加了实体 这一节 写接口及服务类 实现实体的增删改查 创建接口 SIS Application Shared层 创建DTO SIS Application Shared层 对应的Dto文件夹 创建Dto映射Mapper SI
  • vue-el-admin 使用?

    一 搭建 下载 启动 下载地址 panjiachen vue el admin Gitee com 共两个版本 vue element admin 完善版 vue admin template 极简版 启动 node js切换版本到16及以
  • STM32——端口复用与重映射

    目录 端口复用的概念 内置外设的概念 端口复用的概念 端口复用的配置 配置示例 串口1 复用GPIO的配置 STM32中文参考手册 110页 端口重映射概念 端口重映射概念 部分重映射 完全重映射 AFIO时钟 开启AFIO情况 重映射端口
  • 用户注册及登录测试用例小记

    用户注册及登录测试用例
  • 更换PostgreSQL的data并重启服务

    更换 PostgreSQL 的 data 文件夹并重新启动 PostgreSQL 服务 适应场景 系统崩溃 需要恢复 PostgreSQL 数据及服务 平时可用的一种 PostgreSQL 备份 还原手段 操作步骤 导出 PostgreSQ
  • @SpringBootApplication注解学习

    SpringBootApplication 组合注解 具有多个注解功能 SpringBootConfiguration Documented 表明这个注解应该被 javadoc工具记录 没什么用 Configuration 表示当前类可以看
  • (ffmpeg)ffmpeg+SDL的简单播放器(雷霄骅)更新版

    代码源自雷神 一个是播放音频的demo 可以播放MP3和AAC 但是MP3应该是没有封面的 另一个是播放ts格式的视频 没有声音 源码可以到雷神博客下载 但是因为ffmpeg库的更新问题 并不能直接在ubuntu下直接运行 笔者做了修改 在
  • 第九周 【项目3 - 利用二叉树遍历思想解决问题】

    Copyright c 2017 烟台大学计算机与控制工程学院 All rights reservrd 作者 赵楷文 完成时间 2017年10月28日 版本号 v1 0 问题描述 利用二叉树遍历思想解决问题 一 头文件 btree h 包含
  • 支付宝、微信、银联三种支付平台链接

    1 申请支付宝需要的资料 支付宝移动开发平台 1 单位营业执照彩色扫描件或数码照片 2 组织机构代码证彩色扫描件或数码照片 3 对公银行账户 基本账户 一般账户均可 4 法定代表人的身份证彩色扫描件或数码照片 若为代理人 即法人以外的公司代
  • Redis(三)分布式应用

    一 分布式支持 1 性能 Redis本身的QPS已经很高了 但是如果在一些并发量非常高的情况下 性能还是会受到影响 这个时候我们希望有更多的Redis服务来分摊压力 实现负载均衡 2 高可用 如果只有一个Redis服务 一旦服务宕机 那么所