SpringBoot實(shí)現(xiàn)垂直分片的六種策略
一、垂直分片概述
1.1 什么是垂直分片
垂直分片是數(shù)據(jù)庫分庫分表的一種方式,它按照業(yè)務(wù)功能或數(shù)據(jù)表將原本在同一個(gè)數(shù)據(jù)庫的數(shù)據(jù)拆分到不同的數(shù)據(jù)庫實(shí)例中。
與水平分片(將同一張表的數(shù)據(jù)按照某種規(guī)則分散到不同庫或表中)不同,垂直分片主要解決的是業(yè)務(wù)模塊的解耦和單庫的資源瓶頸問題。
1.2 垂直分片的優(yōu)勢
- 減輕單庫壓力:將不同業(yè)務(wù)模塊分散到不同數(shù)據(jù)庫,分散了數(shù)據(jù)庫負(fù)載
- 業(yè)務(wù)解耦:不同業(yè)務(wù)模塊的數(shù)據(jù)獨(dú)立存儲(chǔ),便于業(yè)務(wù)模塊的獨(dú)立擴(kuò)展
- 提高系統(tǒng)可用性:單個(gè)數(shù)據(jù)庫故障只會(huì)影響部分業(yè)務(wù)模塊
- 便于維護(hù)和優(yōu)化:可以針對不同業(yè)務(wù)特點(diǎn)選擇不同的數(shù)據(jù)庫類型和優(yōu)化策略
- 權(quán)限隔離:便于進(jìn)行業(yè)務(wù)級別的數(shù)據(jù)訪問權(quán)限控制
1.3 垂直分片的挑戰(zhàn)
- 分布式事務(wù):跨庫操作時(shí)需要處理分布式事務(wù)
- 數(shù)據(jù)一致性:多庫數(shù)據(jù)同步和一致性保證
- 跨庫查詢:涉及多個(gè)業(yè)務(wù)模塊的查詢變得復(fù)雜
- 開發(fā)復(fù)雜度增加:需要管理多個(gè)數(shù)據(jù)源和業(yè)務(wù)拆分
二、多數(shù)據(jù)源配置策略
2.1 基本原理
多數(shù)據(jù)源配置是實(shí)現(xiàn)垂直分片最直接的方式,通過在SpringBoot中配置多個(gè)DataSource并為不同的業(yè)務(wù)模塊指定不同的數(shù)據(jù)源,實(shí)現(xiàn)業(yè)務(wù)數(shù)據(jù)的物理隔離。
2.2 實(shí)現(xiàn)步驟
2.2.1 配置多個(gè)數(shù)據(jù)源
首先在application.yml中配置多個(gè)數(shù)據(jù)源:
spring:
datasource:
# 用戶服務(wù)數(shù)據(jù)源
user:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://user-db:3306/user_db
username: user_app
password: password
# 訂單服務(wù)數(shù)據(jù)源
order:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://order-db:3306/order_db
username: order_app
password: password
# 產(chǎn)品服務(wù)數(shù)據(jù)源
product:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://product-db:3306/product_db
username: product_app
password: password
2.2.2 創(chuàng)建數(shù)據(jù)源配置類
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.user")
public DataSource userDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.order")
public DataSource orderDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.product")
public DataSource productDataSource() {
return DataSourceBuilder.create().build();
}
}
2.2.3 為不同模塊配置獨(dú)立的事務(wù)管理器和JdbcTemplate
@Configuration
public class UserDbConfig {
@Autowired
@Qualifier("userDataSource")
private DataSource userDataSource;
@Bean
public JdbcTemplate userJdbcTemplate() {
return new JdbcTemplate(userDataSource);
}
@Bean
public PlatformTransactionManager userTransactionManager() {
return new DataSourceTransactionManager(userDataSource);
}
}
@Configuration
public class OrderDbConfig {
@Autowired
@Qualifier("orderDataSource")
private DataSource orderDataSource;
@Bean
public JdbcTemplate orderJdbcTemplate() {
return new JdbcTemplate(orderDataSource);
}
@Bean
public PlatformTransactionManager orderTransactionManager() {
return new DataSourceTransactionManager(orderDataSource);
}
}
@Configuration
public class ProductDbConfig {
@Autowired
@Qualifier("productDataSource")
private DataSource productDataSource;
@Bean
public JdbcTemplate productJdbcTemplate() {
return new JdbcTemplate(productDataSource);
}
@Bean
public PlatformTransactionManager productTransactionManager() {
return new DataSourceTransactionManager(productDataSource);
}
}
2.2.4 在Service層使用不同的數(shù)據(jù)源
@Service
public class UserService {
@Autowired
@Qualifier("userJdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Transactional("userTransactionManager")
public User createUser(User user) {
// 用戶數(shù)據(jù)庫操作
// ...
}
}
@Service
public class OrderService {
@Autowired
@Qualifier("orderJdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Transactional("orderTransactionManager")
public Order createOrder(Order order) {
// 訂單數(shù)據(jù)庫操作
// ...
}
}
2.3 優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
- 配置簡單直觀,容易理解
- 與Spring生態(tài)高度集成
- 對業(yè)務(wù)代碼侵入性低
- 可以針對不同數(shù)據(jù)源配置不同的連接池參數(shù)
缺點(diǎn):
- 無法動(dòng)態(tài)增減數(shù)據(jù)源
- 分布式事務(wù)處理復(fù)雜
- 適合業(yè)務(wù)模塊劃分明確且相對獨(dú)立的場景
- 代碼中需要顯式指定使用哪個(gè)數(shù)據(jù)源和事務(wù)管理器
2.4 適用場景
- 系統(tǒng)初期垂直分片設(shè)計(jì)
- 業(yè)務(wù)模塊劃分明確,跨模塊調(diào)用較少的系統(tǒng)
- 數(shù)據(jù)源數(shù)量較少且相對固定的場景
三、動(dòng)態(tài)數(shù)據(jù)源路由策略
3.1 基本原理
動(dòng)態(tài)數(shù)據(jù)源路由利用Spring提供的AbstractRoutingDataSource類,根據(jù)當(dāng)前執(zhí)行的上下文(如當(dāng)前線程)動(dòng)態(tài)決定使用哪個(gè)數(shù)據(jù)源。
這種方式實(shí)現(xiàn)了數(shù)據(jù)源選擇的透明化,使業(yè)務(wù)代碼無需關(guān)心具體使用哪個(gè)數(shù)據(jù)源。
3.2 實(shí)現(xiàn)步驟
3.2.1 創(chuàng)建數(shù)據(jù)源路由上下文
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
}
3.2.2 實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源路由
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
3.2.3 配置動(dòng)態(tài)數(shù)據(jù)源
@Configuration
public class DynamicDataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.user")
public DataSource userDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.order")
public DataSource orderDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.product")
public DataSource productDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(3);
dataSourceMap.put("user", userDataSource());
dataSourceMap.put("order", orderDataSource());
dataSourceMap.put("product", productDataSource());
// 設(shè)置數(shù)據(jù)源映射
dynamicDataSource.setTargetDataSources(dataSourceMap);
// 設(shè)置默認(rèn)數(shù)據(jù)源
dynamicDataSource.setDefaultTargetDataSource(userDataSource());
return dynamicDataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
3.2.4 創(chuàng)建數(shù)據(jù)源切換注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceSelector {
String value();
}
3.2.5 實(shí)現(xiàn)數(shù)據(jù)源切換的AOP切面
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.example.config.DataSourceSelector)")
public void dataSourcePointcut() {}
@Before("dataSourcePointcut() && @annotation(dataSource)")
public void switchDataSource(JoinPoint point, DataSourceSelector dataSource) {
String dataSourceName = dataSource.value();
DataSourceContextHolder.setDataSourceType(dataSourceName);
}
@After("dataSourcePointcut()")
public void restoreDataSource(JoinPoint point) {
DataSourceContextHolder.clearDataSourceType();
}
}
3.2.6 在Service層使用注解切換數(shù)據(jù)源
@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@DataSourceSelector("user")
@Transactional
public User createUser(User user) {
// 用戶數(shù)據(jù)庫操作
// ...
}
}
@Service
public class OrderService {
@Autowired
private JdbcTemplate jdbcTemplate;
@DataSourceSelector("order")
@Transactional
public Order createOrder(Order order) {
// 訂單數(shù)據(jù)庫操作
// ...
}
}
3.3 優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
- 數(shù)據(jù)源切換對業(yè)務(wù)代碼透明,只需添加注解即可
- 支持動(dòng)態(tài)增減數(shù)據(jù)源
- 可以在運(yùn)行時(shí)根據(jù)不同條件切換數(shù)據(jù)源
- 只需配置一個(gè)事務(wù)管理器,簡化事務(wù)配置
缺點(diǎn):
- 實(shí)現(xiàn)相對復(fù)雜
- 依賴ThreadLocal,在異步或多線程環(huán)境下需要特別處理
- 事務(wù)管理需要注意,避免切換數(shù)據(jù)源導(dǎo)致事務(wù)問題
- 不易實(shí)現(xiàn)分布式事務(wù)
3.4 適用場景
- 業(yè)務(wù)模塊獨(dú)立性不強(qiáng),存在跨模塊訪問需求的系統(tǒng)
- 需要在運(yùn)行時(shí)動(dòng)態(tài)決定數(shù)據(jù)源的場景
- 數(shù)據(jù)源數(shù)量可能動(dòng)態(tài)變化的系統(tǒng)
四、ORM框架多數(shù)據(jù)源配置策略
4.1 基本原理
利用ORM框架(如JPA、MyBatis)的多數(shù)據(jù)源支持,為不同的業(yè)務(wù)模塊配置獨(dú)立的ORM組件,實(shí)現(xiàn)對不同數(shù)據(jù)庫的透明訪問。
這種方式結(jié)合了ORM的便利性和垂直分片的優(yōu)勢。
4.2 實(shí)現(xiàn)步驟(以JPA為例)
4.2.1 配置多個(gè)數(shù)據(jù)源
spring:
# 用戶服務(wù)數(shù)據(jù)源
datasource:
user:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://user-db:3306/user_db
username: user_app
password: password
# 訂單服務(wù)數(shù)據(jù)源
order:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://order-db:3306/order_db
username: order_app
password: password
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
4.2.2 配置用戶模塊的JPA配置
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.user.repository",
entityManagerFactoryRef = "userEntityManagerFactory",
transactionManagerRef = "userTransactionManager"
)
public class UserJpaConfig {
@Autowired
@Qualifier("userDataSource")
private DataSource userDataSource;
@Bean
public LocalContainerEntityManagerFactoryBean userEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(userDataSource)
.packages("com.example.user.entity")
.persistenceUnit("userPU")
.properties(getJpaProperties())
.build();
}
@Bean
public PlatformTransactionManager userTransactionManager(
@Qualifier("userEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
private Map<String, Object> getJpaProperties() {
Map<String, Object> props = new HashMap<>();
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
props.put("hibernate.hbm2ddl.auto", "update");
return props;
}
}
4.2.3 配置訂單模塊的JPA配置
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.order.repository",
entityManagerFactoryRef = "orderEntityManagerFactory",
transactionManagerRef = "orderTransactionManager"
)
public class OrderJpaConfig {
@Autowired
@Qualifier("orderDataSource")
private DataSource orderDataSource;
@Bean
public LocalContainerEntityManagerFactoryBean orderEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(orderDataSource)
.packages("com.example.order.entity")
.persistenceUnit("orderPU")
.properties(getJpaProperties())
.build();
}
@Bean
public PlatformTransactionManager orderTransactionManager(
@Qualifier("orderEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
private Map<String, Object> getJpaProperties() {
Map<String, Object> props = new HashMap<>();
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
props.put("hibernate.hbm2ddl.auto", "update");
return props;
}
}
4.2.4 創(chuàng)建實(shí)體類和Repository
用戶模塊:
// 實(shí)體類
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String email;
// getters and setters
}
// Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByEmail(String email);
}
訂單模塊:
// 實(shí)體類
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long userId;
private BigDecimal amount;
private Date orderDate;
// getters and setters
}
// Repository
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByUserId(Long userId);
}
4.2.5 在Service層使用
@Service
@Transactional("userTransactionManager")
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(User user) {
return userRepository.save(user);
}
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
}
@Service
@Transactional("orderTransactionManager")
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private UserService userService;
public Order createOrder(Order order) {
// 驗(yàn)證用戶是否存在
User user = userService.getUserById(order.getUserId());
if (user == null) {
throw new RuntimeException("User not found");
}
return orderRepository.save(order);
}
public List<Order> getUserOrders(Long userId) {
return orderRepository.findByUserId(userId);
}
}
4.3 優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
- 充分利用ORM框架的便利性
- 實(shí)體類和數(shù)據(jù)庫操作代碼保持簡潔
- 業(yè)務(wù)代碼不需要關(guān)心數(shù)據(jù)源切換
- 對于不同業(yè)務(wù)模塊有清晰的包結(jié)構(gòu)隔離
缺點(diǎn):
- 配置較為復(fù)雜
- 跨模塊查詢需要在Service層手動(dòng)處理
- 不同模塊的事務(wù)無法統(tǒng)一管理,分布式事務(wù)支持有限
- 過多的EntityManagerFactory會(huì)增加內(nèi)存開銷
4.4 適用場景
- 已經(jīng)使用ORM框架的項(xiàng)目
- 業(yè)務(wù)模塊劃分明確,數(shù)據(jù)模型相對獨(dú)立
- 開發(fā)團(tuán)隊(duì)熟悉JPA或其他ORM框架
- 對分布式事務(wù)要求不高的場景
五、分庫中間件策略
5.1 基本原理
利用專業(yè)的分庫分表中間件(如ShardingSphere、MyCat等)進(jìn)行垂直分片,通過中間件提供的路由和代理功能,實(shí)現(xiàn)對多個(gè)數(shù)據(jù)庫的統(tǒng)一管理和訪問。
這種方式將分片邏輯從應(yīng)用層下沉到中間件層,簡化了應(yīng)用開發(fā)。
5.2 實(shí)現(xiàn)步驟(以ShardingSphere-JDBC為例)
5.2.1 添加依賴
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.0</version>
</dependency>
5.2.2 配置垂直分片規(guī)則
spring:
shardingsphere:
datasource:
names: user-db,order-db,product-db
user-db:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://user-db:3306/user_db
username: user_app
password: password
order-db:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://order-db:3306/order_db
username: order_app
password: password
product-db:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://product-db:3306/product_db
username: product_app
password: password
rules:
sharding:
tables:
# 用戶表配置
users:
actual-data-nodes: user-db.users
user_address:
actual-data-nodes: user-db.user_address
# 訂單表配置
orders:
actual-data-nodes: order-db.orders
order_items:
actual-data-nodes: order-db.order_items
# 產(chǎn)品表配置
products:
actual-data-nodes: product-db.products
product_categories:
actual-data-nodes: product-db.product_categories
props:
sql-show: true
5.2.3 創(chuàng)建實(shí)體類和Repository
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String email;
// getters and setters
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 查詢方法
}
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long userId;
private BigDecimal amount;
// getters and setters
}
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByUserId(Long userId);
}
5.2.4 配置數(shù)據(jù)庫訪問
@Configuration
@EnableJpaRepositories(basePackages = "com.example.repository")
@EnableTransactionManagement
public class JpaConfig {
@Bean
public EntityManagerFactory entityManagerFactory(DataSource dataSource) {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.example.entity");
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
return factory.getObject();
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
5.2.5 在Service層使用
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(User user) {
return userRepository.save(user);
}
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
}
@Service
@Transactional
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public Order createOrder(Order order) {
return orderRepository.save(order);
}
public List<Order> getUserOrders(Long userId) {
return orderRepository.findByUserId(userId);
}
}
5.3 優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
- 分片邏輯下沉到中間件,對應(yīng)用層透明
- 統(tǒng)一的事務(wù)管理,部分中間件支持弱XA事務(wù)
- 應(yīng)用代碼無需關(guān)心數(shù)據(jù)源切換
- 支持復(fù)雜的分片規(guī)則和讀寫分離
缺點(diǎn):
- 引入額外的中間件,增加系統(tǒng)復(fù)雜度
- 對SQL有一定限制,部分復(fù)雜查詢可能不支持
- 性能上會(huì)有一定的損耗
- 學(xué)習(xí)成本較高
5.4 適用場景
- 大型系統(tǒng),數(shù)據(jù)量大且需要復(fù)雜分片策略
- 需要同時(shí)支持垂直分片和水平分片的場景
- 對分布式事務(wù)有一定要求的系統(tǒng)
六、微服務(wù)架構(gòu)下的垂直分片策略
6.1 基本原理
在微服務(wù)架構(gòu)中,每個(gè)微服務(wù)擁有自己獨(dú)立的數(shù)據(jù)庫,通過業(yè)務(wù)功能的拆分自然實(shí)現(xiàn)了垂直分片。
不同微服務(wù)之間通過API調(diào)用而非直接數(shù)據(jù)庫訪問進(jìn)行交互,實(shí)現(xiàn)了數(shù)據(jù)的物理隔離和訪問控制。
6.2 實(shí)現(xiàn)步驟
6.2.1 拆分微服務(wù)和數(shù)據(jù)庫
根據(jù)業(yè)務(wù)領(lǐng)域劃分微服務(wù):
- 用戶服務(wù):管理用戶相關(guān)數(shù)據(jù)
- 訂單服務(wù):管理訂單相關(guān)數(shù)據(jù)
- 產(chǎn)品服務(wù):管理產(chǎn)品相關(guān)數(shù)據(jù)
- ...
每個(gè)微服務(wù)擁有獨(dú)立的數(shù)據(jù)庫。
6.2.2 用戶服務(wù)實(shí)現(xiàn)
// 用戶服務(wù)數(shù)據(jù)庫配置
@Configuration
@EnableJpaRepositories(basePackages = "com.example.userservice.repository")
public class UserServiceDbConfig {
// 數(shù)據(jù)源、事務(wù)管理器等配置
}
// 用戶實(shí)體類
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String email;
// getters and setters
}
// 用戶Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 查詢方法
}
// 用戶服務(wù)接口
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
return ResponseEntity.ok(userService.createUser(user));
}
}
6.2.3 訂單服務(wù)實(shí)現(xiàn)
// 訂單服務(wù)數(shù)據(jù)庫配置
@Configuration
@EnableJpaRepositories(basePackages = "com.example.orderservice.repository")
public class OrderServiceDbConfig {
// 數(shù)據(jù)源、事務(wù)管理器等配置
}
// 訂單實(shí)體類
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long userId;
private BigDecimal amount;
// getters and setters
}
// 訂單Repository
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByUserId(Long userId);
}
// 用戶客戶端(Feign)
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
@GetMapping("/api/users/{id}")
User getUserById(@PathVariable Long id);
}
// 訂單服務(wù)
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private UserClient userClient;
public Order createOrder(Order order) {
// 通過Feign調(diào)用用戶服務(wù)驗(yàn)證用戶
User user = userClient.getUserById(order.getUserId());
if (user == null) {
throw new RuntimeException("User not found");
}
return orderRepository.save(order);
}
}
// 訂單API
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping
public ResponseEntity<Order> createOrder(@RequestBody Order order) {
return ResponseEntity.ok(orderService.createOrder(order));
}
@GetMapping("/user/{userId}")
public ResponseEntity<List<Order>> getUserOrders(@PathVariable Long userId) {
return ResponseEntity.ok(orderService.getUserOrders(userId));
}
}
6.3 優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
- 業(yè)務(wù)和數(shù)據(jù)完全解耦,每個(gè)服務(wù)獨(dú)立發(fā)展
- 技術(shù)??梢造`活選擇,不同服務(wù)可以使用不同的數(shù)據(jù)庫類型
- 服務(wù)獨(dú)立部署和擴(kuò)展,提高系統(tǒng)彈性
- 服務(wù)邊界清晰,符合領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)理念
缺點(diǎn):
- 分布式事務(wù)處理復(fù)雜,通常需要采用最終一致性方案
- 跨服務(wù)查詢需要通過API組合數(shù)據(jù),可能影響性能
- 系統(tǒng)復(fù)雜度增加,運(yùn)維成本提高
- 服務(wù)拆分需要充分考慮業(yè)務(wù)邊界,拆分不當(dāng)會(huì)導(dǎo)致頻繁的跨服務(wù)調(diào)用
6.4 適用場景
- 大型復(fù)雜系統(tǒng),需要獨(dú)立擴(kuò)展不同業(yè)務(wù)模塊
- 團(tuán)隊(duì)組織結(jié)構(gòu)與業(yè)務(wù)劃分一致,適合采用微服務(wù)架構(gòu)
- 長期演進(jìn)的系統(tǒng),需要技術(shù)棧靈活性
- 對服務(wù)隔離性和彈性有較高要求的場景
七、策略比較
| 策略 | 復(fù)雜度 | 透明度 | 事務(wù)支持 | 跨庫查詢 | 性能影響 | 維護(hù)成本 |
|---|---|---|---|---|---|---|
| 多數(shù)據(jù)源配置 | 低 | 低 | 單庫事務(wù) | 需代碼處理 | 低 | 低 |
| 動(dòng)態(tài)數(shù)據(jù)源路由 | 中 | 中 | 單庫事務(wù) | 需代碼處理 | 低 | 中 |
| ORM多數(shù)據(jù)源 | 中 | 中 | 單庫事務(wù) | 需代碼處理 | 低 | 中 |
| 分庫中間件 | 高 | 高 | 分布式事務(wù) | 部分支持 | 中 | 高 |
| 微服務(wù)架構(gòu) | 高 | 低 | 最終一致性 | API組合 | 中 | 高 |
注:分布式事務(wù)支持程度取決于所選中間件或自主實(shí)現(xiàn)的數(shù)據(jù)一致性保障機(jī)制
八、垂直分片的最佳實(shí)踐
8.1 數(shù)據(jù)模型設(shè)計(jì)
按業(yè)務(wù)領(lǐng)域劃分表
將相關(guān)性強(qiáng)的表分到同一個(gè)數(shù)據(jù)庫
盡量避免跨庫關(guān)聯(lián)查詢
使用冗余字段減少跨庫依賴
合理使用主鍵
避免使用自增主鍵(特別是在未來可能需要水平分片的場景)
考慮使用UUID或分布式ID生成器
建立合理的索引降低查詢壓力
數(shù)據(jù)冗余與一致性平衡
適當(dāng)冗余關(guān)鍵數(shù)據(jù)降低跨庫查詢
建立數(shù)據(jù)同步機(jī)制保證最終一致性
區(qū)分強(qiáng)一致性場景和最終一致性場景
8.2 事務(wù)處理
本地事務(wù)處理
盡量將事務(wù)限制在單一數(shù)據(jù)庫內(nèi)
單數(shù)據(jù)庫事務(wù)使用Spring的@Transactional注解
分布式事務(wù)策略
對于簡單場景:兩階段提交(XA)
對于高并發(fā)場景:TCC(Try-Confirm-Cancel)
對于長事務(wù)場景:Saga模式
考慮使用Seata等分布式事務(wù)框架
最終一致性實(shí)現(xiàn)
基于消息隊(duì)列的事件驅(qū)動(dòng)架構(gòu)
補(bǔ)償機(jī)制和重試策略
冪等設(shè)計(jì)確保操作可重復(fù)執(zhí)行
8.3 查詢優(yōu)化
減少跨庫查詢
合理劃分業(yè)務(wù)邊界,減少跨業(yè)務(wù)查詢需求
使用數(shù)據(jù)冗余避免頻繁跨庫查詢
考慮使用CQRS模式,為查詢場景構(gòu)建專用視圖
查詢路由策略
分析查詢模式,優(yōu)化分片鍵選擇
對于復(fù)雜查詢,考慮使用查詢視圖或搜索引擎
合理使用緩存減少數(shù)據(jù)庫訪問
結(jié)果聚合處理
在應(yīng)用層對多數(shù)據(jù)源結(jié)果進(jìn)行聚合
使用并行查詢提高效率
結(jié)果分頁和懶加載減少數(shù)據(jù)傳輸量
以上就是SpringBoot實(shí)現(xiàn)垂直分片的六種策略的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot垂直分片的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring中事務(wù)管理的四種方法(銀行轉(zhuǎn)賬為例)
這篇文章主要給大家介紹了關(guān)于Spring中事務(wù)管理的四種方法,文中是以銀行轉(zhuǎn)賬為例,通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
servlet生命周期_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了servlet生命周期的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
Mybatis模糊查詢和動(dòng)態(tài)sql語句的用法
今天小編就為大家分享一篇關(guān)于Mybatis模糊查詢和動(dòng)態(tài)sql語句的用法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03
關(guān)于request.getRequestDispatcher().forward()的妙用及DispatcherType
這篇文章主要介紹了關(guān)于request.getRequestDispatcher().forward()的妙用及DispatcherType對Filter配置的影響,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
Springcloud Nacos基本操作代碼實(shí)例
這篇文章主要介紹了Springcloud Nacos基本操作代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12

