当前位置: 首页 > news >正文

数据库水平分表方案

数据库分表有很多策略,如下:

数据库分表是处理大型数据库中数据量过大的一种常见策略,它可以提高查询性能、减少锁竞争、降低维护成本等。以下是一些常见的数据库分表方案:

1. **垂直分表(Vertical Partitioning)**:
   - 将表中的一部分列拆分到新的表中,通常是根据列的使用频率或者数据类型。
   - 适用于查询中只涉及部分列的情况。

2. **水平分表(Horizontal Partitioning)**:
   - 将表中的行拆分到多个表中,每个表结构相同,但数据行不同。
   - 可以基于某个键值(如用户ID、日期等)进行分片。

3. **基于范围的分表**:
   - 按照数据的某个属性值的范围进行分表,例如按照时间范围(每月一个表)。

4. **基于列表的分表**:
   - 根据某个离散的属性值进行分表,例如用户ID或地区ID。

5. **复合分表**:
   - 结合垂直分表和水平分表,先按列分表,再按行分表。

6. **哈希分表**:
   - 使用哈希函数根据某个键值将数据均匀分配到多个表中。

7. **列表分表**:
   - 根据数据的某个属性值(如枚举类型)将数据分配到不同的表中。

8. **一致性哈希分表**:
   - 适用于分布式系统中,通过一致性哈希算法将数据分配到不同的节点或表中。

9. **分区分表**:
   - 在数据库中创建不同的分区,每个分区可以独立查询。

10. **按业务逻辑分表**:
    - 根据业务模块或逻辑将数据分配到不同的表中。

实施分表策略时,需要考虑以下因素:

- **查询模式**:分析应用的查询模式,确保分表策略能够优化这些查询。
- **数据访问频率**:经常访问的数据应该放在容易访问的位置。
- **数据增长**:预测数据增长趋势,确保分表策略能够适应未来的数据量。
- **维护成本**:分表会增加维护的复杂性,需要权衡性能提升和维护成本。
- **事务一致性**:分表可能会影响事务的处理,需要确保数据一致性。
- **硬件资源**:考虑硬件资源的分配,确保分表策略能够充分利用硬件资源。

在实施分表后,可能还需要使用数据库中间件或应用层的逻辑来管理数据的路由和查询,以确保数据能够正确地分布在不同的表中。


本篇文章主要讲一下水平分表方案

水平分表时,我们需要选一个字段作为分表主键。这种情况下用数据库的自增id肯定不合适了,我们可以使用UUID,或者雪花算法id。

这里我们使用雪花算法生成的id.

在MySQL数据库中,使用雪花算法(Snowflake)作为分表键时,确实存在一些潜在的问题,尤其是在低并发的情况下。雪花算法生成的ID是趋势递增的,但在低并发环境下,由于每次请求的时间戳可能不同,导致生成的ID序列在分表时可能会集中在某些表中,从而造成数据分布不均匀,这种现象称为数据倾斜。

为什么会发生数据倾斜呢?

这是雪花id的组成

| 符号位 | 时间戳 | 工作机器ID | 序列号 |
|--------|--------|------------|---------|
| 0      | 41位    | 10位       | 12位    |

而256张表,二进制位100000000。只有9位

雪花id和256进行与运算或者模运算的时候真正参与运算的只有9位

那么也就是雪花id序列号的部分,那么低并发下,序列号可能一直是一个值或者几个值例如000000000001

000000000001
AND 0000000100000000
-------------------
000000000001

那么这样的情况下就会导致table_1的表存储了大量的数据,而table_2,3,4,5,6......256分不到数据。 

说到数据倾斜,我们需要了解基本的位运算(与、或、非、模)

与(AND)、或(OR)、非(NOT)、模(Modulo)这四种位运算都可以用来确定一个范围内的值,但它们适用的场景和实现方式有所不同。以下是每种运算符如何用于确定一个特定范围内的值:

1. **模运算(Modulo)**:
   模运算是最直接的方式来将一个数值映射到一个给定的范围。例如,如果你有一个很大的数值,你想将其映射到1-256的范围,你可以使用模运算:
   ```java
   int value = someLargeNumber % 256;
   // value 现在是0-255,加1使其变为1-256
   int rangeValue = value + 1;
   ```

2. **与运算(Bitwise AND)**:
   与运算可以用来提取数值的特定位。如果你想要限制数值在一个范围内,你可以使用与运算来获取数值的低位:
   ```java
   int value = someNumber & (256 - 1); // 256 - 1 是0xFF,即11111111二进制
   // value 现在是0-255
   int rangeValue = value + 1; // 使其变为1-256
   ```

3. **或运算(Bitwise OR)**:
   或运算通常用于设置特定位,而不是限制数值范围。不过,你可以结合其他运算来使用或运算。例如,你可以先将数值与一个掩码进行与运算,然后与一个值进行或运算来设置高位:
   ```java
   int value = (someNumber & (256 - 1)) | 0x100; // 将高位设置为1
   // value 现在是256-511,但通常我们不需要这样来确定范围值
   ```

4. **非运算(Bitwise NOT)**:
   非运算用于反转位,它本身不直接用于确定一个范围内的值。但是,它可以与其他位运算结合使用来实现复杂的位操作:
   ```java
   int value = ~someNumber & (256 - 1);
   // 这将反转someNumber的位,然后限制在0-255范围内
   int rangeValue = value + 1; // 使其变为1-256
   ```

在实际应用中,模运算是最常用于将数值映射到特定范围的方法。与运算也可以实现类似的功能,特别是当你想要保留数值的低位时。或运算和非运算通常用于其他类型的位操作,而不是直接用于范围限制,但它们可以与其他运算结合使用来实现复杂的逻辑。

选择哪种运算取决于你的具体需求,例如数据的当前范围、目标范围、以及你是否需要保留数值的某些位。在设计分表策略时,通常会根据数据分布的均匀性和系统的扩展性来选择合适的方法。
 


在数据库分表和分库的场景中,除了基本的位运算(与、或、非、模),以下是一些额外的概念和知识点,它们对于设计和实现高效的分片策略非常重要:

1. **一致性哈希(Consistent Hashing)**:
   一致性哈希是一种特殊的哈希算法,用于分布式系统中,可以在节点(如数据库分片)增加或删除时最小化数据迁移。它通过将数据映射到一个环状空间来实现。

2. **虚拟节点(Virtual Nodes)**:
   在一致性哈希中,为了解决节点分布不均匀的问题,通常会引入虚拟节点。每个物理节点可以对应多个虚拟节点,这些节点均匀分布在哈希环上。

3. **数据倾斜(Data Skew)**:
   数据倾斜是指数据在不同的分片之间分布不均匀,导致某些分片负载过高,而其他分片则相对空闲。需要采取措施来避免或减轻数据倾斜。

4. **范围分片(Range Sharding)**:
   根据数据的某个连续范围(如时间戳、ID范围)来分配到不同的分片。这种方法简单直观,但可能会导致某些分片成为热点。

5. **列表分片(List Sharding)**:
   根据数据的某个离散值(如国家代码、用户类型)来分配到不同的分片。这种方法适用于值域较小且分布均匀的场景。

6. **哈希分片(Hash Sharding)**:
   使用哈希函数将数据均匀分配到不同的分片。这种方法可以很好地分散负载,但需要注意选择合适的哈希函数以避免数据倾斜。

7. **键分片(Key Sharding)**:
   根据数据的某个键值(如用户ID、订单ID)来分配到不同的分片。这种方法可以保持相关数据的局部性,便于查询。

8. **分布式ID生成策略**:
   在分布式系统中,需要生成全局唯一的ID,常用的方法包括UUID、雪花算法(Snowflake)、递增序列等。

9. **跨分片查询(Cross-Shard Query)**:
   当查询条件不落在单个分片的范围内时,可能需要跨多个分片进行查询,这会增加查询的复杂性和成本。

10. **分布式事务管理**:
    在分片的环境中,保持事务的一致性是一个挑战。可能需要使用两阶段提交(2PC)、补偿事务(TCC)或最终一致性模型。

11. **数据迁移策略**:
    随着业务的发展,可能需要对分片进行扩容或缩容,这涉及到数据的迁移。需要设计高效的数据迁移策略以最小化对业务的影响。

12. **元数据管理**:
    在分片环境中,需要管理分片的元数据,如分片的映射关系、分片的健康状态等。

了解这些概念和策略有助于在设计分库分表方案时做出更合理的决策,从而提高系统的性能、可扩展性和稳定性。
 


雪花算法为了避免数据倾斜,导致部分数据全部存储到几张表中。

随机化序列号:在低并发情况下,可以通过在算法中引入随机化序列号来使ID分布更加均匀。例如,美团的Leaf分布式ID生成服务就采用了这种方法,通过在每个毫秒内为序列号添加一个随机偏移量来实现ID的随机化分布。

美团的代码https://github.com/Meituan-Dianping/Leaf/blob/master/leaf-core/src/main/java/com/sankuai/inf/leaf/snowflake/SnowflakeIDGenImpl.java

package com.sankuai.inf.leaf;import com.sankuai.inf.leaf.common.Result;public interface IDGen {Result get(String key);boolean init();
}
package com.sankuai.inf.leaf.snowflake;import com.google.common.base.Preconditions;
import com.sankuai.inf.leaf.IDGen;
import com.sankuai.inf.leaf.common.Result;
import com.sankuai.inf.leaf.common.Status;
import com.sankuai.inf.leaf.common.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.Random;public class SnowflakeIDGenImpl implements IDGen {@Overridepublic boolean init() {return true;}private static final Logger LOGGER = LoggerFactory.getLogger(SnowflakeIDGenImpl.class);private final long twepoch;private final long workerIdBits = 10L;private final long maxWorkerId = ~(-1L << workerIdBits);//最大能够分配的workerid =1023private final long sequenceBits = 12L;private final long workerIdShift = sequenceBits;private final long timestampLeftShift = sequenceBits + workerIdBits;private final long sequenceMask = ~(-1L << sequenceBits);private long workerId;private long sequence = 0L;private long lastTimestamp = -1L;private static final Random RANDOM = new Random();public SnowflakeIDGenImpl(String zkAddress, int port) {//Thu Nov 04 2010 09:42:54 GMT+0800 (中国标准时间) this(zkAddress, port, 1288834974657L);}/*** @param zkAddress zk地址* @param port      snowflake监听端口* @param twepoch   起始的时间戳*/public SnowflakeIDGenImpl(String zkAddress, int port, long twepoch) {this.twepoch = twepoch;Preconditions.checkArgument(timeGen() > twepoch, "Snowflake not support twepoch gt currentTime");final String ip = Utils.getIp();SnowflakeZookeeperHolder holder = new SnowflakeZookeeperHolder(ip, String.valueOf(port), zkAddress);LOGGER.info("twepoch:{} ,ip:{} ,zkAddress:{} port:{}", twepoch, ip, zkAddress, port);boolean initFlag = holder.init();if (initFlag) {workerId = holder.getWorkerID();LOGGER.info("START SUCCESS USE ZK WORKERID-{}", workerId);} else {Preconditions.checkArgument(initFlag, "Snowflake Id Gen is not init ok");}Preconditions.checkArgument(workerId >= 0 && workerId <= maxWorkerId, "workerID must gte 0 and lte 1023");}@Overridepublic synchronized Result get(String key) {long timestamp = timeGen();if (timestamp < lastTimestamp) {long offset = lastTimestamp - timestamp;if (offset <= 5) {try {wait(offset << 1);timestamp = timeGen();if (timestamp < lastTimestamp) {return new Result(-1, Status.EXCEPTION);}} catch (InterruptedException e) {LOGGER.error("wait interrupted");return new Result(-2, Status.EXCEPTION);}} else {return new Result(-3, Status.EXCEPTION);}}if (lastTimestamp == timestamp) {sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {//seq 为0的时候表示是下一毫秒时间开始对seq做随机sequence = RANDOM.nextInt(100);timestamp = tilNextMillis(lastTimestamp);}} else {//如果是新的ms开始sequence = RANDOM.nextInt(100);}lastTimestamp = timestamp;long id = ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;return new Result(id, Status.SUCCESS);}protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}protected long timeGen() {return System.currentTimeMillis();}public long getWorkerId() {return workerId;}}

 sequence = RANDOM.nextInt(100);

就是对每毫秒起始的sequence取随值,美团的随机范围是0到100。最终的效果就是生成的id会均匀分布在tb_0到tb_100。而我们如果分表数是256,则需要改成

sequence = RANDOM.nextInt(256);

或者用下面这个简化版本

import java.util.concurrent.ThreadLocalRandom;public class RandomizedSnowflakeIdWorker {private long lastTimestamp = -1L;private long sequence = 0L;private final long workerIdBits = 5L;private final long datacenterIdBits = 5L;private final long maxWorkerId = -1L ^ (-1L << workerIdBits);private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);private final long sequenceBits = 12L;private final long workerIdShift = sequenceBits;private final long datacenterIdShift = sequenceBits + workerIdBits;private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;private final long sequenceMask = -1L ^ (-1L << sequenceBits);private final long twepoch = 1288834974657L;private long workerId;private long datacenterId;public RandomizedSnowflakeIdWorker(long workerId, long datacenterId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}if (lastTimestamp == timestamp) {sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {timestamp = tilNextMillis(lastTimestamp);}} else {sequence = ThreadLocalRandom.current().nextLong(sequenceMask + 1);}lastTimestamp = timestamp;return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;}protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}protected long timeGen() {return System.currentTimeMillis();}public static void main(String[] args) {RandomizedSnowflakeIdWorker idWorker = new RandomizedSnowflakeIdWorker(0, 0);for (int i = 0; i < 1000; i++) {long id = idWorker.nextId();System.out.println(Long.toBinaryString(id));System.out.println(id);}}
}

在这个示例中,sequence 在每次调用 nextId 方法时都会随机化,而不是自增。这是通过 ThreadLocalRandom.current().nextLong(sequenceMask + 1) 实现的,它会在每个毫秒内生成一个随机的序列号。这样可以确保在高并发情况下,生成的ID更加分散,减少数据倾斜的风险。

请注意,这个示例是一个简化的版本,实际应用中可能需要更复杂的逻辑来确保ID的全局唯一性和趋势递增性。此外,时钟回拨的处理也需要根据实际需求进行设计。

雪花id+hash散列表

int tableIndex = (int)(snowflakeId.hashCode() & 0xFFFFFFFF) % 256;


关于雪花id时钟回拨的问题有一下几个解决方案

雪花算法(Snowflake)是一种广泛使用的分布式唯一ID生成方法,它通过结合时间戳、机器ID和序列号来生成一个64位的长整型ID。这种算法能够确保在分布式系统中生成全局唯一的ID,并且具有高性能和高可用性的特点。

然而,雪花算法的一个潜在问题是时钟回拨,即服务器时间意外地回退到之前的时间。这可能导致生成重复的ID,因为算法依赖于时间戳来保证ID的唯一性。以下是几种解决时钟回拨问题的策略:

1. **直接抛出异常**:
   如果检测到时钟回拨,算法可以拒绝生成新的ID并直接抛出异常。这是一种简单直接的方法,但可能会导致服务中断。

2. **等待策略**:
   当检测到时钟回拨时,服务可以等待直到系统时钟恢复到正常状态。这种方法可以确保ID的严格递增性,但可能会在时钟调整期间暂停服务。

3. **序列号持久化**:
   将序列号持久化存储(例如在数据库中),这样即使发生时钟回拨,也可以从持久化存储中恢复最后一个序列号,从而继续生成新的ID。

4. **使用历史时间戳**:
   在某些实现中,可以使用一个“历史时间戳”来代替当前时间戳,每次请求只增加序列号,当序列号用完时,再增加历史时间戳。

5. **增加容忍时钟回拨的时间阈值**:
   在算法中设置一个容忍时钟回拨的时间阈值,如果回拨时间在这个阈值内,算法可以等待或采取其他措施,而不是立即抛出异常。

6. **备用机方案**:
   如果当前机器出现时钟回拨,可以尝试切换到备用机器上继续提供服务。

7. **采用之前最大时间**:
   当检测到时钟回拨时,可以采用之前记录的最大时间戳和序列号继续生成ID。

8. **基于时钟序列的方案**:
   将机器ID拆分为时钟序列和机器码,发生时间回拨时,增加时钟序列的值,从而生成新的ID。

这些策略可以单独使用,也可以组合使用,以提供更强的容错能力和更高的可用性。在实际应用中,应根据业务需求和系统特性选择最合适的解决方案。
 


在分布式系统中,确保时钟同步的准确无误是至关重要的,因为时间的一致性对于事件的顺序、数据的一致性和系统的协调运行都有着直接的影响。以下是一些确保分布式系统中时钟同步准确无误的策略:

1. **使用网络时间协议(NTP)**:NTP 是一种广泛使用的时钟同步协议,它允许分布式系统中的每个节点与一个或多个时间服务器进行通信,以同步其时钟。NTP 通过逐步逼近的方式来减少时差,确保了整个系统内各个节点的时钟同步。

2. **配置NTP服务器**:在分布式集群中,可以设置一个或多个服务器作为NTP服务器,其他节点则作为客户端定期从这些服务器同步时间。如果集群中的所有节点都能访问互联网,可以直接与公共NTP服务器同步。如果只有部分节点可以访问互联网,可以选择其中一个作为时间服务器,其他节点从这个时间服务器同步时间。

3. **手动时间同步**:在某些情况下,如果集群中的节点无法访问互联网,可能需要手动设置服务器节点的时间,并定期进行校准。

4. **使用精确时间协议(PTP)**:在需要更高时间精度的场合,可以使用PTP。PTP通过主从架构在主时钟和从时钟之间交换同步信息,利用特殊的时间戳和硬件支持来减少测量延迟,实现高精度同步。

5. **向量时钟和逻辑时钟**:向量时钟和逻辑时钟是解决分布式系统中时钟同步问题的软件方案。它们通过为每个事件分配一个时间戳来记录事件的因果关系,从而在没有物理时钟同步的情况下保持事件的一致性顺序。

6. **TrueTime和混合逻辑时钟(HLC)**:TrueTime是Google提出的概念,用于在分布式系统中提高物理时钟的可靠性。HLC结合了逻辑时钟和物理时钟的优点,支持对事件进行因果关系排序,同时又有物理时钟直观的特点。

7. **监控和告警**:对系统时钟进行监控,并在检测到时钟回拨或偏差超过阈值时触发告警,以便运维人员可以及时发现问题并进行处理。

8. **优化ID生成算法**:除了引入容忍时钟回拨的阈值外,还可以根据实际需求对ID生成算法进行优化,以提高ID生成速度和唯一性。

通过上述方法,可以有效地确保分布式系统中的时钟同步,从而保障系统的稳定运行和数据一致性。
 

相关文章:

数据库水平分表方案

数据库分表有很多策略&#xff0c;如下&#xff1a; 数据库分表是处理大型数据库中数据量过大的一种常见策略&#xff0c;它可以提高查询性能、减少锁竞争、降低维护成本等。以下是一些常见的数据库分表方案&#xff1a; 1. **垂直分表&#xff08;Vertical Partitioning&…...

MySQL表操作及约束

修改表 重命名&#xff1a; mysql> alter table user1 rename to user; 新增一列 mysql> alter table user add image_path varchar(128) comment 路径 after birthday; mysql> alter table user add image_path varchar(128) comment 路径 after birthday; Query…...

Redis 键值对操作全攻略

文章目录 一 . get 和 set二 . keys *三 . exists四 . del五 . expire六 . ttl七 . Redis 的 key 的过期策略八 . 定时器的实现8.1 基于优先级队列8.2 基于时间轮实现的定时器 九 . type十 . 数据库管理相关命令 Hello , 大家好 , 这个专栏给大家带来的是 Redis 系列 ! 本篇文章…...

【C语言】---- return的作用

return 是C语言中的一个关键字&#xff0c;用于从函数中返回值。它有以下几个作用&#xff1a; 1 返回值 return 用于将函数的结果返回给调用者。在函数执行过程中&#xff0c;当遇到 return 语句时&#xff0c;函数将立即停止执行&#xff0c;并将其后的表达式的值作为函数的…...

如何制作新生资料收集系统?

新学年伊始&#xff0c;学校需要高效收集学生信息和证件照。易查分提供了一个便捷的解决方案&#xff0c;通过创建一个集成信息和图片的收集系统&#xff0c;可以快速完成这项工作&#xff0c;并将信息导出为PDF&#xff0c;方便打印和存档。 制作步骤如下&#xff1a; 1. 准备…...

pyecharts可视化数据大屏

1. 简介 1.1. 概述 PyECharts 是一个用于生成 ECharts 图表的 Python 库。ECharts 是一款由阿里巴巴开发的开源 JavaScript 图表库,广泛应用于网页端的数据可视化。而 PyECharts 将 ECharts 的功能带到了 Python 环境中,使得 Python 开发者可以方便地在数据分析、机器学习等…...

uniapp - H5 在 UC 浏览器中返回上一页失效的解决方案

1. 前言 最近使用 uniapp 开发的 H5 在测试中遇到的问题:有A、B两个页面,反复从A页面进入B页面后,在B页面会存在返回函数失效问题。经过测试发现,如果反复进入B页面的参数相同,在第三次进入后,返回就会失效,不管是 uniapp 自带的头部导航,还是 uni.navigateBack API 都…...

利用KMeans重新计算自己数据集的anchor

在YOLOv5或YOLOv7中&#xff0c;anchors&#xff08;锚框&#xff09;是预设的一组不同大小、不同长宽比的边界框&#xff0c;它们用于在图像中的每个网格单元上进行偏移和缩放&#xff0c;以生成目标的候选框。这些anchors的设定对于提高目标检测的效率和准确性至关重要。 并…...

分类任务实现模型集成代码模版

分类任务实现模型&#xff08;投票式&#xff09;集成代码模版 简介 本实验使用上一博客的深度学习分类模型训练代码模板-CSDN博客&#xff0c;自定义投票式集成&#xff0c;手动实现模型集成&#xff08;投票法&#xff09;的代码。最后通过tensorboard进行可视化&#xff0…...

从Milvus迁移DashVector

本文档演示如何从Milvus将Collection数据全量导出&#xff0c;并适配迁移至DashVector。方案的主要流程包括&#xff1a; 首先&#xff0c;升级Milvus版本&#xff0c;目前Milvus只有在最新版本(v.2.3.x)中支持全量导出其次&#xff0c;将Milvus Collection的Schema信息和数据…...

彻底改变计算机视觉的 Vision Transformer (ViT) 综合指南(视觉转换器终极指南)

欢迎来到雲闪世界。大家好&#xff01;对于那些还不认识我的人&#xff0c;我叫 Francois&#xff0c;我是 Meta 的研究科学家。我热衷于解释先进的 AI 概念并使其更容易理解。 今天&#xff0c;让我们深入探讨计算机视觉领域最重要的贡献之一&#xff1a;Vision Transformer&…...

vue3 v-bind=“$attrs“ 的一些理解,透传 Attributes相关说明及事例说明

1、可能小伙伴们经常会在自己的项目中看到v-bind"$attrs"&#xff0c;这个一般是在自定义组件中看到。 比如&#xff1a; <template><BasicModalv-bind"$attrs"register"registerModal":title"getTitle"ok"handleSubm…...

鸿蒙开发基础知识-页面布局【第四篇】

1.类型转换 2.交互点击事件 3.状态管理 4.forEch渲染和右上角图标 测试案例 Stack 层叠布局一个生肖卡 5. 动画展示图片 6. Swiper 轮播组件的基本使用 图片等比显示 aspectRatio&#xff08;&#xff09;...

用CSS实现前端响应式布局

一、响应式布局的重要性 随着移动设备的普及&#xff0c;越来越多的用户通过手机、平板电脑等设备访问网页。如果网页不能适应不同的屏幕尺寸&#xff0c;就会出现布局混乱、内容显示不全等问题&#xff0c;严重影响用户体验。响应式布局可以确保网页在各种设备上都能保持美观…...

【docker】docker启动sqlserver

sqlserver-docker官方地址 # sqlserver不是从docker的中央仓库拉取的&#xff0c;而是从ms的仓库拉取的。 docker pull mcr.microsoft.com/mssql/server:2019-latest# 宿主机即docker程序运行的linux服务器 docker run -d \ --user root \ --name mssql2019 \ -e "ACCEPT…...

Python爬虫01

requests模块 文档 安装 pip/pip3 install requestsresponse.text 和 response.content的区别 1.response.text 等价于 response.content.decode("推测出的编码字符集")response.text 类型&#xff1a;str 编码类型&#xff1a;requests模块自动根据Http头部对…...

关于vue项目启动报错Error: error:0308010C:digital envelope routines::unsupported

周五啦&#xff0c;总结一下这周遇到的个别问题吧&#xff0c;就是关于启动项目的时候其他的东西都准备好了&#xff0c;执行命令后报错Error: error:0308010C:digital envelope routines::unsupported 这里看一下我标注的地方&#xff0c;然后总结一下就不难发现问题所在 查看…...

随笔1:数学建模与数值计算

目录 1.1 矩阵运算 1.2 基本数学函数 1.3 数值求解 数学建模与数值计算 是将实际问题通过数学公式和模型进行描述&#xff0c;并通过计算获得模型解的过程。这是数学建模中最基本也是最重要的环节之一。下面是详细的知识点讲解及相应的MATLAB代码示例。 1.1 矩阵运算 知识点…...

SDN架构详解

目录 1&#xff09;经典的IP网络-分布式网络 2&#xff09;经典网络面临的问题 3&#xff09;SDN起源 4&#xff09;OpenFlow基本概念 5&#xff09;Flow Table简介 6&#xff09;SDN的网络架构 7&#xff09;华为SDN网络架构 8&#xff09;传统网络 vs SDN 9&#xf…...

platform框架

platform框架 注册设备进入总线platform_device_register函数 注册驱动进入总线platform_driver_register函数 注册设备进入总线 platform_device_register函数 int platform_device_register(struct platform_device *pdev) struct platform_device {const char * name; 名…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...