SpringBoot+ShardingSphere-分庫分表教程(一)

日常使用數據庫的時候,更多的時間是在關心業務功能的實現,為了盡快完成新版本的發布上線,通常在項目初期不太會去在意數據庫的壓力和性能問題。在服務上線一段時間之后,就會發現當初設計存在著很多的不足,這都是項目研發的正常過程。對于有經驗的程序員,在項目設計初期就會想到將來有一天會遇到這些問題,所以就從一開始就將代碼寫的比較完善,這也是提現大齡程序員優勢的地方。

有一種常見的現象就是,項目上線初期,由于用戶量不多,所以數據庫中的數據也不會太多,服務運行的非常順暢,但是隨著生產數據的積累,很快就導致了數據庫性能瓶頸的到來。這時候我們最先想到的是提升數據庫的資源,加大內存,加大磁盤,從而度過數據庫壓力的難關,但是這畢竟是暫時的,比如一張表里的數據量迅速的增長,而且你又不能刪除這里面的數據,終究有一天會讓這張表爆掉。

以mysql為例,如果單張表的行數超過500萬行的時候,通常就能感受到非常明顯的性能衰減,這點不得不佩服oracle動輒幾億的單表查詢能力,但是沒辦法,兩者價格的差距也是性能的差距。如何應對這種持續增長的單表數據呢?一種常用的方式就是分庫分表,就是把一張巨大的表,按照一定的規則分到不同的表里去,這樣每張分表的數據量就小了,從而保證每個分表的性能,如果分表也不足以支撐大數據量,就通過分庫,把數據量分到多個庫里去,從而支撐住業務功能。

shardingsphere是諸多分庫分表工具中比較優秀的一款,在我經歷過的公司中,也應用在了生產服務中,雖然使用過程中遇到的坑也不少,不過總體來說,還是足夠支撐業務功能。我們首先介紹一下,分庫和分表是兩個截然不同的功能,雖然總混在一起說,分表只要我們在Springboot中引入shardingsphere-jdbc這個依賴庫即可,但是分庫就要單獨部署一個服務shardingsphere-proxy,其他服務連接shardingsphere-proxy,從而實現分庫的功能。

我們先用shardingsphere-jdbc來進行單庫的分表,分表常用的規則有兩種,一種是通過時間進行分表,比如一個月一張表,或者一周一張表,另外一種就根據列的數值進行分表,比如id是1-1000用一張表,1001-2000用一張表,分表的規則要按照業務功能去切分,無論哪種分表策略,最終的目標就是讓數據均勻的分布在各個分表中。

1、創建數據表

我們先創建一張存儲消息的表,過去我們創建消息表就是一張,比如叫sys_message,但是現在我們是用分表,所以就要創建一批表,我們設定消息表使用時間分表策略,每7天一張表,從2025年1月1日開始,所以我們就要創建一張分表sys_message_20250101,然后按照每7天一張表創建出多干個消息分表,這里注意,shardingsphere的分表默認是不自動創建表的,所以我們先手動創建,我制作了一個存儲過程可以快速創建出多張sys_message分表。

sys_message_20250101這一張是分表的基礎表,沒有啟動會報錯,其他的分表即使沒有,啟動的時候也不報錯,但是用到了就會拋出異常。

CREATE TABLE `sys_message_20250101` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',`msg` longblob COMMENT '消息內容',`version` int NOT NULL DEFAULT '1' COMMENT '版本號',`is_logic_delete` int NOT NULL DEFAULT '0' COMMENT '邏輯刪除',`create_by` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '創建人',`create_time` datetime DEFAULT NULL COMMENT '創建時間',`update_by` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人',`update_time` datetime DEFAULT NULL COMMENT '修改時間',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1989 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='系統-消息表';

自動創建從2025年1月1日起到一年后的分表存儲過程:

CREATE PROCEDURE `sp_generate_message_tables`(IN start_date DATE)
BEGINDECLARE end_date DATE DEFAULT DATE_ADD(CURRENT_DATE(), INTERVAL 1 YEAR);DECLARE item_date DATE;DECLARE table_name VARCHAR(50);SET item_date = DATE_ADD(start_date, INTERVAL 7 DAY);WHILE item_date <= end_date DOSET table_name = CONCAT('sys_message_', DATE_FORMAT(item_date, '%Y%m%d'));SET @sql = CONCAT('CREATE TABLE IF NOT EXISTS ', table_name, ' LIKE sys_message_', DATE_FORMAT(start_date, '%Y%m%d'));PREPARE stmt FROM @sql;EXECUTE stmt;DEALLOCATE PREPARE stmt;SET item_date = DATE_ADD(item_date, INTERVAL 7 DAY);END WHILE;SELECT CONCAT('分表生成完成,時間范圍:', start_date, ' 至 ', end_date) AS result;
END

2、創建項目shardingsphere-demo

創建一個新項目shardingsphere-demo,并且在pom.xml文件中引入MyBatis-Plus、Shardingsphere和mysql依賴。

<dependencies><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.10.1</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-jsqlparser</artifactId><version>3.5.10.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.4.0</version></dependency><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc</artifactId><version>5.5.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.24</version></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.54</version></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.4.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>RELEASE</version><scope>compile</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>

3、創建MyBatis-Plus的各個類

雖然各個分表的表名是不一樣的,但是在代碼里我們并不用去記錄這些表名,而是使用邏輯表名sys_message進行操作,讓Shardingsphere去自動幫我們定位真正的分表。

MessageDO:

package com.mj.shardingsphere.entity;import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.time.LocalDateTime;/*** 系統-消息表*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "sys_message")
public class MessageDO implements Serializable {/*** id*/@TableId(value = "id", type = IdType.ASSIGN_ID)private Long id;/*** 消息value*/@TableField(value = "msg")private String msg;/*** 版本號*/@Version@TableField(value = "version")private Integer version;/*** 邏輯刪除*/@TableLogic@TableField(value = "is_logic_delete")private Integer logicDelete;/*** 創建人*/@TableField(value = "create_by", fill = FieldFill.INSERT)private String createBy;/*** 創建時間*/@TableField(value = "create_time", fill = FieldFill.INSERT)private LocalDateTime createTime;/*** 修改人*/@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)private String updateBy;/*** 修改時間*/@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;private static final long serialVersionUID = 1L;
}

MessageMapper:

package com.mj.shardingsphere.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mj.shardingsphere.entity.MessageDO;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface MessageMapper extends BaseMapper<MessageDO> {
}

MessageMapper.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mj.shardingsphere.dao.MessageMapper"><resultMap id="BaseResultMap" type="com.mj.shardingsphere.entity.MessageDO"><!--@mbg.generated--><!--@Table sys_message--><id column="id" jdbcType="BIGINT" property="id" /><result column="msg" jdbcType="VARCHAR" property="msg" /><result column="version" jdbcType="INTEGER" property="version" /><result column="is_logic_delete" jdbcType="INTEGER" property="logicDelete" /><result column="create_by" jdbcType="VARCHAR" property="createBy" /><result column="create_time" jdbcType="TIMESTAMP" property="createTime" /><result column="update_by" jdbcType="VARCHAR" property="updateBy" /><result column="update_time" jdbcType="TIMESTAMP" property="updateTime" /></resultMap><sql id="Base_Column_List"><!--@mbg.generated-->id, msg, version, is_logic_delete, create_by, create_time, update_by, update_time</sql>
</mapper>

4、Shardingsphere配置:

創建一個文件sharding.yml,上半部分就是數據庫的配置,將數據源和連接池交給了Sharding進行管理,Springboot里面就不用再配置了。sys_message_algorithm決定了分表的策略,按照時間分表的時候,要定好分表的時間段,可以寫一個很長的時間。sharding-suffix-pattern是分表的后綴格式,正是因為有這個配置,Sharding才能很好的把所有的分表整合成了一個邏輯表讓我們用分表的時候就像只有一張表一樣。最后就是配置分表時間是7天,這時候數據庫里的分表也要嚴格按照7的跨度去生成。

# 模式配置
mode:type: Standalonerepository:type: JDBC
# 數據源配置
dataSources:sharding:dataSourceClassName: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.18.42:3306/sharding?useSSL=false&useUnicode=true&characterEncoding=UTF-8username: rootpassword: rootdruid:test-on-borrow: truevalidation-query: SELECT 1 FROM DUALweb-stat-filter:enabled: truestat-view-servlet:enabled: truelogin-username: druidlogin-password: 12345pool-prepared-statements: falsemax-pool-prepared-statement-per-connection-size: 20
# 規則配置
rules:# 單表配置- !SINGLEtables:- sharding.*# 數據分片- !SHARDINGtables:sys_message:actualDataNodes: sharding.sys_message_${20250101..20991231}tableStrategy:standard: # 用于單分片鍵的標準分片場景shardingColumn: create_timeshardingAlgorithmName: sys_message_algorithmkeyGenerateStrategy: # 分布式序列策略column: idkeyGeneratorName: snowflakeauditStrategy: # 分片審計策略auditorNames: # 分片審計算法名稱- sharding_key_required_auditorallowHintDisable: true# 分片算法配置shardingAlgorithms:sys_message_algorithm:type: INTERVALprops:datetime-pattern: yyyy-MM-dd HH:mm:ssdatetime-lower: "2025-01-01 00:00:00"  # 添加引號確保格式正確datetime-upper: "2099-12-31 23:59:59"  # 添加引號確保格式正確sharding-suffix-pattern: yyyyMMdddatetime-interval-amount: 7datetime-interval-unit: DAYS# 分布式序列算法配置keyGenerators:snowflake:type: SNOWFLAKE# 分片審計算法配置auditors:sharding_key_required_auditor:type: DML_SHARDING_CONDITIONSprops:sql-show: true
#  sql-simple: false
#  max-connections-size-per-query: 1
#  check-table-metadata-enabled: false

修改一下application.yml文件,把sharding.yml文件引入進去。

spring:application:name: shardingsphere-demodatasource:type: com.alibaba.druid.pool.DruidDataSource# 引入shardingspheredriver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriverurl: jdbc:shardingsphere:classpath:sharding.yml?placeholder-type=environmentinitialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: 'SELECT 1 FROM DUAL'testWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: truemaxPoolPreparedStatementPerConnectionSize: 20filters: 'stat,wall'connectionProperties: 'druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000'useGlobalDataSourceStat: truemybatis-plus:configuration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:id-type: auto# ????logic-delete-field: deletedlogic-delete-value: 1logic-not-delete-value: 0mapper-locations: classpath:/mapper/**.xmlspringdoc:swagger-ui:path: /swagger-ui.htmltags-sorter: alphaoperations-sorter: alphaapi-docs:path: /v3/api-docsgroup-configs:- group: 'default'paths-to-match: '/**'packages-to-scan: com.mj.shardingsphere
knife4j:enable: trueproduction: falsesetting:language: zh_cn

5、編寫服務類

MessageService:

package com.mj.shardingsphere.service;import com.mj.shardingsphere.entity.MessageDO;import java.util.List;public interface MessageService {String sendMessage(String message);List<MessageDO> getMessages();
}

MessageServiceImpl:

這里要注意查詢的使用,由于shardingsphere是從分表里進行操作,所以查詢的時候也是從所有的分表里進行查詢,這是一件很恐怖的事情,所以一定要默認帶著分片鍵并且固定一個區間,讓它從有限的分表里進行查詢,防止因為查詢太多數據而導致服務宕機。這里可能會給業務功能帶來一定的困擾,比如就是不知道查詢多久數據,這時候必須要在業務功能上做讓步。

package com.mj.shardingsphere.service.impl;import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.mj.shardingsphere.dao.MessageMapper;
import com.mj.shardingsphere.entity.MessageDO;
import com.mj.shardingsphere.service.MessageService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;import java.time.LocalDateTime;
import java.util.List;@AllArgsConstructor
@Service
public class MessageServiceImpl implements MessageService {private final MessageMapper messageMapper;@Overridepublic String sendMessage(String message) {MessageDO messageDO = new MessageDO();messageDO.setMsg(message);messageDO.setCreateBy("SYSTEM");messageDO.setCreateTime(LocalDateTime.now());messageDO.setUpdateBy("SYSTEM");messageDO.setUpdateTime(LocalDateTime.now());messageMapper.insert(messageDO);return "ok";}@Overridepublic List<MessageDO> getMessages() {//查詢分表的時候,一定要使用分片鍵去固定分表的區間,防止查詢太多的表return messageMapper.selectList(Wrappers.lambdaQuery(MessageDO.class).ge(MessageDO::getCreateTime, LocalDateTime.now().minusMonths(1)).le(MessageDO::getCreateTime, LocalDateTime.now()));}
}

6、測試接口

MessageController:

package com.mj.shardingsphere.controller;import com.mj.shardingsphere.entity.MessageDO;
import com.mj.shardingsphere.service.MessageService;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@AllArgsConstructor
@RequestMapping("/message")
@RestController
public class MessageController {private final MessageService messageService;@GetMapping("/add")public String add() {return messageService.sendMessage("message-" + System.currentTimeMillis());}@GetMapping("/list")public List<MessageDO> lst() {return messageService.getMessages();}
}

http://127.0.0.1:8080/message/add?通過調用add接口,通過日志和數據庫,能發現進入到對應時間段內的那個分表了。

http://127.0.0.1:8080/message/list?查詢接口查詢了最近一個月的數據

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/86875.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/86875.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/86875.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

INA226 電流計 功率計電路圖轉PCB制作

上次發布了TI的INA226電路圖&#xff0c;今天抽了點時間&#xff0c;把電路圖生成了PCB。 帖出來&#xff0c;不足之處&#xff0c;請兄弟們留言指正。 沒什么問題就可以去嘉立創白嫖了。^_^

Vcpkg 經典模式完整遷移方案

&#x1f680; 從零開始&#xff1a;高效使用 Vcpkg 安裝 Qt WebEngine&#xff08;經典模式 緩存優化 性能釋放&#xff09; &#x1f9e9; 背景簡介 在使用 Vcpkg 安裝 Qt 系列庫時&#xff0c;特別是龐大的 qtwebengine 模塊&#xff0c;編譯量極大&#xff0c;耗時可達…

FPGA產品

FPGA產品 文章目錄 FPGA產品1. Xilinx公司FPGA產品2. Altera公司FPGA產品3. FPGA產品的工業等級簡介4. FPGA產品的速度等級簡介總結 1. Xilinx公司FPGA產品 Xilinx公司是FPGA芯片的發明者&#xff0c;因此是一家骨灰級的老牌FPGA公司&#xff0c;同時也是目前最大的可編程邏輯…

205-06-26 Python深度學習1——安裝Anaconda與PyTorch庫(Win11+WSL2+Ubuntu24.04版)

文章目錄 1 安裝 wsl1.1 開啟 Windows 支持1.2 安裝 wsl1.3 移動 wsl 至其他盤1.4 其他事項 2 安裝 Anaconda3 安裝 Python 環境3.1 創建 Conda 環境3.2 安裝 Pytorch 庫&#xff08;gpu&#xff09; 4 安裝 Pycharm4.1 Toolbox App 安裝4.2 安裝 Pycharm4.3 配置 Pycharm 5 測…

Redis 數據遷移同步:應對大 Key 同步挑戰

在企業級的數據同步和遷移場景中&#xff0c;Redis 憑借高性能和靈活的數據結構&#xff0c;常被用于緩存和高頻讀寫場景。隨著業務數據的積累&#xff0c;Redis 中不可避免會出現包含大量元素的“大 Key”&#xff0c;如包含幾十萬條數據的 List、Set 或 Hash 類型。在進行全量…

視頻關鍵幀提取

&#x1f39e;? 視頻關鍵幀提取與特征分析指南 &#x1f4cc; 抽幀數量建議 視頻時長推薦抽幀數原因短視頻&#xff08;≤15秒&#xff09;3&#xff5e;5 幀覆蓋不同場景即可中長視頻&#xff08;1&#xff5e;3分鐘&#xff09;5&#xff5e;10 幀內容跨度大長視頻&#xf…

協作機器人優化自動化工作流程,提升工作效率

無損檢測(NDT)是一種檢查方法&#xff0c;用于識別材料中的裂紋或缺陷&#xff0c;或者在不損壞材料的情況下確定材料的元素組成。Olympus擁有多種NDT設備&#xff0c;這些設備具有多種多樣的測量功能&#xff0c;允許最終用戶對各種行業中使用的金屬、塑料、陶瓷和復合材料進行…

復用對象Aspose.Words 中 DocumentBuilder 的狀態管理解析

doc manager.LoadDocument(filePath) builder.Document doc 是不是builder就自動清空重建了,不需要清理builder Aspose.Words 中 DocumentBuilder 的狀態管理解析 在您的代碼中&#xff0c;builder.Document doc 這行代碼不會自動清空或重建DocumentBuilder的狀態。Docume…

(LeetCode 面試經典 150 題 ) 134. 加油站 (貪心)

題目&#xff1a;134. 加油站 思路&#xff1a;貪心&#xff0c;時間復雜度0(n)。 當前點i來到下一個點i1,那么油的變化量是gas[i]-cost[i]。 先統計遍歷完所有點后&#xff0c;油的變化量sum。如果sum<0&#xff0c;說明不可能繞行一周&#xff1b;sum>0&#xff0c;說…

Java 線程池總結

一、寫在前面 參考阿里開發規約,創建線程池一般用ThreadPoolExecutor 在高并發程序中&#xff0c;頻繁創建與銷毀線程是一種極其低效且不可控的行為。為了解決這個問題&#xff0c;Java 提供了線程池&#xff08;ThreadPoolExecutor&#xff09;這一強大的并發框架。它不僅提…

【3.3】Pod詳解——容器探針部署第一個pod

文章目錄 容器探針小知識-控制平面Pod實戰聲明式模型&命令模式 部署第一個pod編寫pod清單文件kubectl命令將清單文件post到api-server驗證pod刪除pod 容器探針 上面已經講到容器狀態,那么這些容器的狀態是怎么檢測到的呢?實際上在pod中有三種探針&#xff0c;存活探針(li…

Insar 相位展開真實的數據集的生成與下載(隨機矩陣放大,zernike 仿真包裹相位)

1.真實的數據集下載: Delta-X: UAVSAR L1B Interferometric Products, MRD, Louisiana, 2021 | NASA Earthdata 注意下載的時候需要注冊登錄一下哦 2. 適用于 深度學習訓練的數據集 通過網盤分享的文件:InSAR-DLPU.rar 鏈接: https://pan.baidu.com/s/1CRWAuNYwCHP_iqCeIhf…

C++ 多線程深度解析:掌握并行編程的藝術與實踐

在現代軟件開發中&#xff0c;多線程&#xff08;multithreading&#xff09;已不再是可選項&#xff0c;而是提升應用程序性能、響應速度和資源利用率的核心技術。隨著多核處理器的普及&#xff0c;如何讓代碼有效地利用這些硬件資源&#xff0c;成為每個 C 開發者必須掌握的技…

(線性代數)矩陣的奇異值Singular Value

矩陣的奇異值是矩陣分析中一個非常重要的概念&#xff0c;尤其是在數值線性代數、數據降維&#xff08;如PCA&#xff09;、圖像處理等領域有著廣泛應用。奇異值分解&#xff08;SVD, Singular Value Decomposition&#xff09;是一種強大的工具&#xff0c;可以將任意形狀的矩…

數據結構復習4

第四章 串 一些面試題 12. 介紹一下KMP算法。★★★ KMP算法是一種高效的字符串匹配算法&#xff0c;用于在一個文本串中查找一個模式串的出現位置。KMP算法通過利用模式串自身的信息&#xff0c;在匹配過程中避免不必要的回溯&#xff0c;從而提高匹配效率。 KMP算法的核心思…

【八股消消樂】消息隊列優化—消息有序

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一個正在變禿、變強的文藝傾年。 &#x1f514;本專欄《八股消消樂》旨在記錄個人所背的八股文&#xff0c;包括Java/Go開發、Vue開發、系統架構、大模型開發、具身智能、機器學習、深度學習、力扣算法等相關知識點&#xff…

2D寫實交互數字人如何重塑服務體驗?

在數字化浪潮席卷全球的當下&#xff0c;人機交互模式正經歷著前所未有的變革。從早期的文本命令行界面&#xff0c;到圖形用戶界面&#xff08;GUI&#xff09;的普及&#xff0c;再到如今語音交互、手勢識別等多模態交互技術的興起&#xff0c;我們與機器之間的溝通方式愈發自…

CI/CD GitHub Actions配置流程

騰訊云服務器寶塔FinalShellgithup 1.在云服務器上創建SSH秘鑰對&#xff0c;下載秘鑰到本地 2.在服務器中綁定秘鑰對&#xff08;綁定后&#xff0c;服務器不能將不允許密碼登錄&#xff09;綁定前先關機服務器&#xff0c;綁定后再開啟服務器 3.FinalShell改為公鑰登錄&am…

液態交互效果網頁開發--源自鴻蒙5以及iOS26的靈感

首先先來看看最終展示效果 當鼠標靠近“開始探索”的按鈕的時候&#xff0c;按鈕放大并有微弱光效 鼠標靠近之前會給視窗添加一層接近背景的朦朧感&#xff0c;當鼠標放在視窗上朦朧感消失 技術不復雜&#xff0c;這個網頁主要是使用了以下關鍵技術&#xff1a; HTML5 語義化標…

PYTHON從入門到實踐9-類和實例

# 【1】面向對象編程 class Student(object):# 可以幫屬性值綁定到對象上&#xff0c;self相當于JAVA的thisdef __init__(self, name, age):self.name nameself.age agedef speak(self):print(self.name, 說&#xff1a;老師好)if __name__ __main__:new_student1 Student(…