ShardingProxy讀寫分離之原理、配置與實(shí)踐過(guò)程
一、ShardingProxy技術(shù)定位與讀寫分離核心價(jià)值
1.1 技術(shù)定位
ShardingProxy是Apache ShardingSphere生態(tài)中的數(shù)據(jù)庫(kù)中間件,采用客戶端-代理-數(shù)據(jù)庫(kù)的三層架構(gòu)模式,對(duì)應(yīng)用程序透明(無(wú)需修改應(yīng)用代碼),負(fù)責(zé)接收應(yīng)用的SQL請(qǐng)求并進(jìn)行路由轉(zhuǎn)發(fā)。其核心定位是:作為分布式數(shù)據(jù)庫(kù)的“流量入口”,解決單機(jī)數(shù)據(jù)庫(kù)在高并發(fā)場(chǎng)景下的性能瓶頸,支持讀寫分離、分庫(kù)分表、數(shù)據(jù)脫敏等核心能力,在微服務(wù)架構(gòu)、高流量業(yè)務(wù)系統(tǒng)(如電商訂單、用戶中心)中廣泛應(yīng)用。
在整個(gè)技術(shù)體系中,ShardingProxy處于“應(yīng)用層與數(shù)據(jù)庫(kù)層之間的中間件層”,上接應(yīng)用服務(wù)的SQL請(qǐng)求,下連主從架構(gòu)的數(shù)據(jù)庫(kù)集群,承擔(dān)SQL解析、路由決策、結(jié)果合并等關(guān)鍵職責(zé),是實(shí)現(xiàn)數(shù)據(jù)庫(kù)水平擴(kuò)展的核心組件之一。
1.2 讀寫分離核心價(jià)值
在傳統(tǒng)單機(jī)數(shù)據(jù)庫(kù)架構(gòu)中,“讀”和“寫”操作均依賴同一臺(tái)數(shù)據(jù)庫(kù),當(dāng)業(yè)務(wù)流量增長(zhǎng)(如秒殺活動(dòng)、高頻查詢場(chǎng)景)時(shí),讀請(qǐng)求會(huì)大量占用數(shù)據(jù)庫(kù)資源,導(dǎo)致寫操作響應(yīng)延遲,甚至引發(fā)數(shù)據(jù)庫(kù)性能雪崩。
ShardingProxy讀寫分離的核心價(jià)值在于拆分讀寫流量:
- 將“寫操作”(INSERT/UPDATE/DELETE)路由至主庫(kù),保證數(shù)據(jù)一致性;
- 將“讀操作”(SELECT)路由至從庫(kù),利用從庫(kù)集群分擔(dān)讀壓力;
- 支持從庫(kù)負(fù)載均衡,避免單從庫(kù)成為新瓶頸;
- 提供故障轉(zhuǎn)移能力,當(dāng)某臺(tái)從庫(kù)下線時(shí),自動(dòng)將讀流量切換至其他可用從庫(kù)。
二、ShardingProxy讀寫分離核心原理
2.1 整體工作流程
ShardingProxy實(shí)現(xiàn)讀寫分離的核心流程分為5個(gè)步驟,具體如下:
| 步驟 | 核心操作 | 說(shuō)明 |
|---|---|---|
| 1 | SQL接收 | 應(yīng)用通過(guò)JDBC/ODBC協(xié)議連接ShardingProxy(代理地址與端口),發(fā)送SQL請(qǐng)求 |
| 2 | SQL解析 | ShardingProxy對(duì)SQL進(jìn)行語(yǔ)法解析、語(yǔ)義分析,判斷SQL類型(讀/寫) |
| 3 | 路由決策 | 根據(jù)預(yù)設(shè)的讀寫分離規(guī)則,決定將請(qǐng)求路由至主庫(kù)(寫操作)或從庫(kù)(讀操作);若為讀操作,還需通過(guò)負(fù)載均衡算法選擇具體從庫(kù) |
| 4 | 請(qǐng)求轉(zhuǎn)發(fā) | 將解析后的SQL轉(zhuǎn)發(fā)至目標(biāo)數(shù)據(jù)庫(kù)(主庫(kù)/從庫(kù)) |
| 5 | 結(jié)果返回 | 接收目標(biāo)數(shù)據(jù)庫(kù)的執(zhí)行結(jié)果,若涉及多從庫(kù)(如分頁(yè)查詢合并)則進(jìn)行結(jié)果處理,最終返回給應(yīng)用 |
2.2 關(guān)鍵技術(shù)點(diǎn)
2.2.1 SQL類型判斷
ShardingProxy通過(guò)SQL語(yǔ)法樹解析區(qū)分讀寫操作:
- 寫操作判定:包含INSERT、UPDATE、DELETE、CREATE、ALTER等會(huì)修改數(shù)據(jù)或表結(jié)構(gòu)的SQL;
- 讀操作判定:僅包含SELECT的SQL(特殊場(chǎng)景如SELECT … FOR UPDATE會(huì)被判定為寫操作,需單獨(dú)配置)。
2.2.2 從庫(kù)負(fù)載均衡算法
ShardingProxy支持3種常用負(fù)載均衡算法,滿足不同業(yè)務(wù)場(chǎng)景需求:
- 輪詢算法(ROUND_ROBIN):按從庫(kù)順序依次分配讀請(qǐng)求,適用于各從庫(kù)配置一致的場(chǎng)景;
- 隨機(jī)算法(RANDOM):隨機(jī)選擇從庫(kù)分配請(qǐng)求,適用于從庫(kù)性能存在輕微差異的場(chǎng)景;
- 權(quán)重算法(WEIGHT):根據(jù)從庫(kù)權(quán)重分配請(qǐng)求(權(quán)重越高,接收請(qǐng)求越多),適用于從庫(kù)配置差異較大的場(chǎng)景(如高性能從庫(kù)權(quán)重設(shè)為10,普通從庫(kù)設(shè)為5)。
2.2.3 主從數(shù)據(jù)一致性保障
由于主從復(fù)制存在延遲(MySQL默認(rèn)異步復(fù)制),可能導(dǎo)致“寫主庫(kù)后立即讀從庫(kù)”出現(xiàn)數(shù)據(jù)不一致問(wèn)題。ShardingProxy提供2種解決方案:
- 強(qiáng)制路由主庫(kù):對(duì)核心業(yè)務(wù)讀請(qǐng)求(如用戶下單后查詢訂單狀態(tài)),通過(guò)Hint語(yǔ)法強(qiáng)制路由至主庫(kù);
- 主從延遲控制:配置從庫(kù)延遲閾值(如500ms),當(dāng)從庫(kù)延遲超過(guò)閾值時(shí),自動(dòng)將讀請(qǐng)求路由至主庫(kù),避免臟讀。
三、ShardingProxy讀寫分離環(huán)境搭建與配置
3.1 前置環(huán)境準(zhǔn)備
搭建ShardingProxy讀寫分離需滿足以下環(huán)境依賴,以MySQL主從架構(gòu)為例:
| 組件 | 版本要求 | 說(shuō)明 |
|---|---|---|
| JDK | 1.8及以上 | ShardingProxy基于Java開發(fā),需配置JAVA_HOME |
| MySQL | 5.7/8.0 | 需提前搭建MySQL主從復(fù)制(異步/半同步均可) |
| ShardingProxy | 4.1.1(穩(wěn)定版) | 從Apache官網(wǎng)下載 |
MySQL主從復(fù)制驗(yàn)證
確保主從復(fù)制正常運(yùn)行,在主庫(kù)執(zhí)行以下SQL創(chuàng)建測(cè)試表并插入數(shù)據(jù),從庫(kù)需能同步數(shù)據(jù):
-- 主庫(kù)創(chuàng)建測(cè)試庫(kù)
CREATE DATABASE IF NOT EXISTS sharding_db;
USE sharding_db;
-- 創(chuàng)建用戶表
CREATE TABLE `t_user` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL,
`age` INT NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 插入測(cè)試數(shù)據(jù)(從庫(kù)需同步該數(shù)據(jù))
INSERT INTO t_user (username, age) VALUES ('zhangsan', 25), ('lisi', 30);
3.2 ShardingProxy核心配置
ShardingProxy的配置文件位于conf目錄下,核心配置文件為server.yaml(全局配置)和config-sharding.yaml(讀寫分離+分庫(kù)分表配置)。
3.2.1 server.yaml配置(全局基礎(chǔ)配置)
# 服務(wù)端口(應(yīng)用連接ShardingProxy的端口)
serverPort: 3307
# 管理端口(用于監(jiān)控和運(yùn)維)
adminPort: 8088
# 數(shù)據(jù)庫(kù)驅(qū)動(dòng)類名
driverClassName: com.mysql.cj.jdbc.Driver
# 認(rèn)證配置(應(yīng)用連接ShardingProxy的賬號(hào)密碼)
authentication:
users:
root:
password: 123456 # 應(yīng)用連接密碼
sharding:
password: sharding
authorizedSchemas: sharding_db # 授權(quán)訪問(wèn)的數(shù)據(jù)庫(kù)
# 日志配置(輸出SQL路由日志,便于調(diào)試)
props:
max.connections.size.per.query: 1
acceptor.size: 16 # 用于接收連接的線程數(shù)
executor.size: 16 # 用于處理SQL的線程數(shù)
proxy.frontend.flush.threshold: 128 # 前端刷盤閾值
proxy.transaction.type: LOCAL
proxy.opentracing.enabled: false
query.with.cipher.column: true
sql.show: true # 開啟SQL顯示(打印路由后的SQL,調(diào)試必備)
3.2.2 config-sharding.yaml配置(讀寫分離核心配置)
# 數(shù)據(jù)源配置(主庫(kù)+從庫(kù))
dataSources:
# 主庫(kù)數(shù)據(jù)源
master_ds:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://192.168.1.100:3306/sharding_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: root
password: root123
connectionTimeout: 30000
idleTimeout: 60000
maxLifetime: 1800000
maximumPoolSize: 50
# 從庫(kù)1數(shù)據(jù)源
slave_ds_0:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://192.168.1.101:3306/sharding_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: root
password: root123
connectionTimeout: 30000
idleTimeout: 60000
maxLifetime: 1800000
maximumPoolSize: 50
# 從庫(kù)2數(shù)據(jù)源(可擴(kuò)展多個(gè)從庫(kù))
slave_ds_1:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://192.168.1.102:3306/sharding_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: root
password: root123
connectionTimeout: 30000
idleTimeout: 60000
maxLifetime: 1800000
maximumPoolSize: 50
# 讀寫分離規(guī)則配置
rules:
- !READWRITE_SPLITTING
dataSources:
# 讀寫分離數(shù)據(jù)源名稱(應(yīng)用實(shí)際訪問(wèn)的數(shù)據(jù)源名)
sharding_rw_ds:
type: Static
props:
write-data-source-name: master_ds # 寫操作路由至主庫(kù)
read-data-source-names: slave_ds_0,slave_ds_1 # 讀操作路由至兩個(gè)從庫(kù)
load-balance-algorithm-type: ROUND_ROBIN # 從庫(kù)負(fù)載均衡算法(輪詢)
# 可選:主從延遲控制(超過(guò)500ms則讀主庫(kù))
# read-write-splitting-delay-threshold: 500
# 綁定表規(guī)則(無(wú)分表時(shí)可省略,此處僅作示例)
bindingTables:
- t_user
# 默認(rèn)數(shù)據(jù)源(當(dāng)SQL未匹配任何規(guī)則時(shí),路由至該數(shù)據(jù)源)
defaultDataSourceName: sharding_rw_ds
3.3 ShardingProxy啟動(dòng)與驗(yàn)證
3.3.1 啟動(dòng)ShardingProxy
進(jìn)入ShardingProxy的bin目錄;
執(zhí)行啟動(dòng)腳本(Windows用start.bat,Linux用start.sh):
# Linux啟動(dòng)命令(指定配置文件目錄) ./start.sh ../conf
驗(yàn)證啟動(dòng)狀態(tài):查看logs/stdout.log日志,若出現(xiàn)以下內(nèi)容則啟動(dòng)成功:
[INFO ] 2024-05-20 10:00:00.000 [main] o.a.s.p.frontend.ShardingSphereProxy - ShardingSphere-Proxy start success.
3.3.2 連接驗(yàn)證(使用Navicat/MySQL客戶端)
連接參數(shù):
- 主機(jī):ShardingProxy所在服務(wù)器IP(如192.168.1.103);
- 端口:3307(對(duì)應(yīng)
server.yaml中的serverPort); - 賬號(hào):root(對(duì)應(yīng)
server.yaml中的認(rèn)證用戶); - 密碼:123456(對(duì)應(yīng)
server.yaml中的認(rèn)證密碼); - 數(shù)據(jù)庫(kù):sharding_db(授權(quán)數(shù)據(jù)庫(kù))。
執(zhí)行讀寫操作驗(yàn)證:
-- 1. 寫操作(INSERT):查看ShardingProxy日志,確認(rèn)路由至master_ds
INSERT INTO t_user (username, age) VALUES ('wangwu', 35);
-- 2. 讀操作(SELECT):查看日志,確認(rèn)輪詢路由至slave_ds_0、slave_ds_1
SELECT * FROM t_user;
-- 3. 強(qiáng)制讀主庫(kù)(Hint語(yǔ)法):核心業(yè)務(wù)讀請(qǐng)求用此方式保障一致性
/*!SHARDINGSPHERE_HINT: DATA_SOURCE_NAME=master_ds */
SELECT * FROM t_user WHERE id = 3;
四、工程實(shí)踐:SpringBoot整合ShardingProxy讀寫分離
4.1 項(xiàng)目依賴配置(pom.xml)
在SpringBoot項(xiàng)目中添加MySQL驅(qū)動(dòng)和JDBC依賴(無(wú)需添加ShardingSphere客戶端依賴,因應(yīng)用僅需連接ShardingProxy,無(wú)需感知中間件細(xì)節(jié)):
<dependencies>
<!-- SpringBoot JDBC Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- MySQL驅(qū)動(dòng) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
<scope>runtime</scope>
</dependency>
<!-- 數(shù)據(jù)庫(kù)連接池(HikariCP) -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<!-- SpringBoot Test(用于測(cè)試) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
4.2 數(shù)據(jù)庫(kù)配置(application.yml)
應(yīng)用連接ShardingProxy的配置,與連接普通MySQL數(shù)據(jù)庫(kù)一致(無(wú)需修改代碼,完全透明):
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.1.103:3307/sharding_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: root
password: 123456
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
4.3 業(yè)務(wù)代碼實(shí)現(xiàn)(用戶模塊示例)
4.3.1 實(shí)體類(User.java)
public class User {
private Long id;
private String username;
private Integer age;
// 無(wú)參構(gòu)造、有參構(gòu)造、getter、setter
public User() {}
public User(String username, Integer age) {
this.username = username;
this.age = age;
}
// getter和setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
}
4.3.2 DAO層(UserDao.java)
使用JdbcTemplate實(shí)現(xiàn)數(shù)據(jù)訪問(wèn)(MyBatis/MyBatis-Plus用法一致,僅需配置數(shù)據(jù)源):
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.util.List;
@Repository
public class UserDao {
@Resource
private JdbcTemplate jdbcTemplate;
// 寫操作:新增用戶(路由至主庫(kù))
public int addUser(User user) {
String sql = "INSERT INTO t_user (username, age) VALUES (?, ?)";
return jdbcTemplate.update(sql, user.getUsername(), user.getAge());
}
// 讀操作:查詢所有用戶(路由至從庫(kù),輪詢)
public List<User> listAllUsers() {
String sql = "SELECT id, username, age FROM t_user";
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
}
// 讀操作:強(qiáng)制讀主庫(kù)(核心業(yè)務(wù),保障數(shù)據(jù)一致性)
public User getUserById(Long id) {
// Hint語(yǔ)法:強(qiáng)制路由至主庫(kù)
String sql = "/*!SHARDINGSPHERE_HINT: DATA_SOURCE_NAME=master_ds */ " +
"SELECT id, username, age FROM t_user WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), id);
}
}
4.4 測(cè)試代碼(UserDaoTest.java)
通過(guò)單元測(cè)試驗(yàn)證讀寫分離效果:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
public class UserDaoTest {
@Resource
private UserDao userDao;
// 測(cè)試寫操作(路由至主庫(kù))
@Test
public void testAddUser() {
User user = new User("zhaoliu", 40);
int rows = userDao.addUser(user);
assertEquals(1, rows); // 新增成功返回1
}
// 測(cè)試讀操作(路由至從庫(kù),輪詢)
@Test
public void testListAllUsers() {
List<User> userList = userDao.listAllUsers();
assertNotNull(userList);
System.out.println("查詢用戶數(shù)量:" + userList.size());
// 連續(xù)查詢3次,觀察ShardingProxy日志,確認(rèn)從庫(kù)輪詢(slave_ds_0 → slave_ds_1 → slave_ds_0)
for (int i = 0; i < 3; i++) {
userDao.listAllUsers();
}
}
// 測(cè)試強(qiáng)制讀主庫(kù)(保障數(shù)據(jù)一致性)
@Test
public void testGetUserById() {
// 先新增用戶(寫主庫(kù))
User user = new User("qianqi", 28);
userDao.addUser(user);
// 立即查詢剛新增的用戶(強(qiáng)制讀主庫(kù),避免主從延遲導(dǎo)致查不到數(shù)據(jù))
User result = userDao.getUserById(user.getId());
assertNotNull(result);
assertEquals("qianqi", result.getUsername());
}
}
## 五、常見問(wèn)題與解決方案
### 5.1 主從數(shù)據(jù)不一致
#### 問(wèn)題表現(xiàn)
應(yīng)用執(zhí)行“寫主庫(kù)后立即讀從庫(kù)”時(shí),可能查詢不到最新數(shù)據(jù)(因主從復(fù)制存在延遲)。
#### 解決方案
1. **強(qiáng)制路由主庫(kù)**:對(duì)核心業(yè)務(wù)場(chǎng)景(如用戶注冊(cè)后立即查詢用戶信息),使用Hint語(yǔ)法強(qiáng)制讀主庫(kù):
```sql
/*!SHARDINGSPHERE_HINT: DATA_SOURCE_NAME=master_ds */
SELECT * FROM t_user WHERE id = ?
配置延遲閾值:在config-sharding.yaml中設(shè)置read-write-splitting-delay-threshold(單位:毫秒),當(dāng)從庫(kù)延遲超過(guò)閾值時(shí),自動(dòng)路由至主庫(kù):
props: read-write-splitting-delay-threshold: 500 # 從庫(kù)延遲>500ms時(shí)讀主庫(kù)
5.2 從庫(kù)故障導(dǎo)致讀失敗
問(wèn)題表現(xiàn)
某臺(tái)從庫(kù)宕機(jī)或網(wǎng)絡(luò)故障時(shí),ShardingProxy若仍將請(qǐng)求路由至該從庫(kù),會(huì)導(dǎo)致讀操作失敗。
解決方案
ShardingProxy默認(rèn)支持從庫(kù)故障檢測(cè)與自動(dòng)剔除,需在config-sharding.yaml中配置健康檢查參數(shù):
dataSources:
slave_ds_0:
# 其他配置省略...
health:
check:
enable: true # 開啟健康檢查
timeout: 3000 # 檢查超時(shí)時(shí)間(毫秒)
interval: 5000 # 檢查間隔(毫秒)
slave_ds_1:
# 同上配置...
配置后,當(dāng)從庫(kù)連續(xù)3次健康檢查失敗,ShardingProxy會(huì)將其從可用列表中剔除;恢復(fù)正常后,自動(dòng)重新加入。
5.3 SQL路由錯(cuò)誤
問(wèn)題表現(xiàn)
某些特殊SQL(如SELECT ... FOR UPDATE)被誤判為讀操作,路由至從庫(kù)導(dǎo)致執(zhí)行失?。◤膸?kù)默認(rèn)只讀)。
解決方案
在config-sharding.yaml中配置SQL路由規(guī)則,修正特殊SQL的路由邏輯:
rules:
- !READWRITE_SPLITTING
dataSources:
sharding_rw_ds:
# 其他配置省略...
props:
# 自定義SQL路由規(guī)則:將包含F(xiàn)OR UPDATE的SELECT視為寫操作
sql-parser-rule: "SELECT.*FOR UPDATE"
sql-parser-rule-type: WRITE # 匹配上述規(guī)則的SQL視為寫操作
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Gradle build 報(bào)錯(cuò):Received status code 400 from server
這篇文章主要介紹了Gradle build 報(bào)錯(cuò):Received status code 400 from server,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
在ChatGPT的API中支持多輪對(duì)話的實(shí)現(xiàn)方法
ChatGPT是由OpenAI研發(fā)的一種預(yù)訓(xùn)練語(yǔ)言模型,只能在OpenAI平臺(tái)上進(jìn)行訓(xùn)練,目前并不對(duì)外開放訓(xùn)練接口,這篇文章主要介紹了在ChatGPT的API中支持多輪對(duì)話的實(shí)現(xiàn)方法,需要的朋友可以參考下2023-02-02
Java實(shí)現(xiàn)仿淘寶滑動(dòng)驗(yàn)證碼研究代碼詳解
這篇文章主要介紹了Java實(shí)現(xiàn)仿淘寶滑動(dòng)驗(yàn)證碼研究代碼詳解的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06
IDEA報(bào)錯(cuò)之前言中不允許有內(nèi)容問(wèn)題及解決
當(dāng)使用IntelliJ IDEA時(shí),可能會(huì)遇到報(bào)錯(cuò)信息“前言中不允許有內(nèi)容”,這通常是由于XML文件是以帶有BOM頭的UTF-8格式保存的,導(dǎo)致IDE的解析出錯(cuò),解決辦法是在IDEA中調(diào)整文件編碼設(shè)置為無(wú)BOM的UTF-8,然后用文本編輯器(如Notepad++)2024-10-10
intellij idea查看方法被哪些類引用過(guò)(推薦)
這篇文章主要介紹了intellij idea查看方法被哪些類引用過(guò),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
Spark?Streaming?內(nèi)部運(yùn)行機(jī)制示例詳解
這篇文章主要介紹了Spark?Streaming?內(nèi)部運(yùn)行機(jī)制示例詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2025-05-05
Java 實(shí)用工具類Spring 的 AnnotationUtils詳解
Spring 框架提供了一個(gè)強(qiáng)大的注解工具類 org.springframework.core.annotation.AnnotationUtils,用于簡(jiǎn)化注解的獲取、合成與解析過(guò)程,本文給大家介紹Java 實(shí)用工具類:Spring 的 AnnotationUtils,感興趣的朋友一起看看吧2025-04-04

