随着业务规模的不断扩大,单一数据库架构往往难以满足日益增长的数据量和访问压力。
作为解决方案之一,垂直分片通过将不同业务模块的数据分散到不同的数据库或实例中,有效缓解了数据库的性能瓶颈。
本文将介绍在SpringBoot环境下实现垂直分片的六种策略。
一、垂直分片概述
1.1 什么是垂直分片
垂直分片是数据库分库分表的一种方式,它按照业务功能或数据表将原本在同一个数据库的数据拆分到不同的数据库实例中。
与水平分片(将同一张表的数据按照某种规则分散到不同库或表中)不同,垂直分片主要解决的是业务模块的解耦和单库的资源瓶颈问题。
1.2 垂直分片的优势
- 减轻单库压力:将不同业务模块分散到不同数据库,分散了数据库负载
- 业务解耦:不同业务模块的数据独立存储,便于业务模块的独立扩展
- 提高系统可用性:单个数据库故障只会影响部分业务模块
- 便于维护和优化:可以针对不同业务特点选择不同的数据库类型和优化策略
- 权限隔离:便于进行业务级别的数据访问权限控制
1.3 垂直分片的挑战
- 分布式事务:跨库操作时需要处理分布式事务
- 数据一致性:多库数据同步和一致性保证
- 跨库查询:涉及多个业务模块的查询变得复杂
- 开发复杂度增加:需要管理多个数据源和业务拆分
二、多数据源配置策略
2.1 基本原理
多数据源配置是实现垂直分片最直接的方式,通过在SpringBoot中配置多个DataSource并为不同的业务模块指定不同的数据源,实现业务数据的物理隔离。
2.2 实现步骤
2.2.1 配置多个数据源
首先在application.yml
中配置多个数据源:
yaml
spring:
datasource:
# 用户服务数据源
user:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://user-db:3306/user_db
username: user_app
password: password
# 订单服务数据源
order:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://order-db:3306/order_db
username: order_app
password: password
# 产品服务数据源
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 创建数据源配置类
kotlin
@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 为不同模块配置独立的事务管理器和JdbcTemplate
typescript
@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层使用不同的数据源
less
@Service
public class UserService {
@Autowired
@Qualifier("userJdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Transactional("userTransactionManager")
public User createUser(User user) {
// 用户数据库操作
// ...
}
}
@Service
public class OrderService {
@Autowired
@Qualifier("orderJdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Transactional("orderTransactionManager")
public Order createOrder(Order order) {
// 订单数据库操作
// ...
}
}
2.3 优缺点分析
优点:
- 配置简单直观,容易理解
- 与Spring生态高度集成
- 对业务代码侵入性低
- 可以针对不同数据源配置不同的连接池参数
缺点:
- 无法动态增减数据源
- 分布式事务处理复杂
- 适合业务模块划分明确且相对独立的场景
- 代码中需要显式指定使用哪个数据源和事务管理器
2.4 适用场景
- 系统初期垂直分片设计
- 业务模块划分明确,跨模块调用较少的系统
- 数据源数量较少且相对固定的场景
三、动态数据源路由策略
3.1 基本原理
动态数据源路由利用Spring提供的AbstractRoutingDataSource
类,根据当前执行的上下文(如当前线程)动态决定使用哪个数据源。
这种方式实现了数据源选择的透明化,使业务代码无需关心具体使用哪个数据源。
3.2 实现步骤
3.2.1 创建数据源路由上下文
typescript
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 实现动态数据源路由
scala
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
3.2.3 配置动态数据源
typescript
@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());
// 设置数据源映射
dynamicDataSource.setTargetDataSources(dataSourceMap);
// 设置默认数据源
dynamicDataSource.setDefaultTargetDataSource(userDataSource());
return dynamicDataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
3.2.4 创建数据源切换注解
less
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceSelector {
String value();
}
3.2.5 实现数据源切换的AOP切面
less
@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层使用注解切换数据源
less
@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@DataSourceSelector("user")
@Transactional
public User createUser(User user) {
// 用户数据库操作
// ...
}
}
@Service
public class OrderService {
@Autowired
private JdbcTemplate jdbcTemplate;
@DataSourceSelector("order")
@Transactional
public Order createOrder(Order order) {
// 订单数据库操作
// ...
}
}
3.3 优缺点分析
优点:
- 数据源切换对业务代码透明,只需添加注解即可
- 支持动态增减数据源
- 可以在运行时根据不同条件切换数据源
- 只需配置一个事务管理器,简化事务配置
缺点:
- 实现相对复杂
- 依赖ThreadLocal,在异步或多线程环境下需要特别处理
- 事务管理需要注意,避免切换数据源导致事务问题
- 不易实现分布式事务
3.4 适用场景
- 业务模块独立性不强,存在跨模块访问需求的系统
- 需要在运行时动态决定数据源的场景
- 数据源数量可能动态变化的系统
四、ORM框架多数据源配置策略
4.1 基本原理
利用ORM框架(如JPA、MyBatis)的多数据源支持,为不同的业务模块配置独立的ORM组件,实现对不同数据库的透明访问。
这种方式结合了ORM的便利性和垂直分片的优势。
4.2 实现步骤(以JPA为例)
4.2.1 配置多个数据源
yaml
spring:
# 用户服务数据源
datasource:
user:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://user-db:3306/user_db
username: user_app
password: password
# 订单服务数据源
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配置
less
@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配置
less
@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 创建实体类和Repository
用户模块:
less
// 实体类
@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);
}
订单模块:
less
// 实体类
@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层使用
kotlin
@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) {
// 验证用户是否存在
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 优缺点分析
优点:
- 充分利用ORM框架的便利性
- 实体类和数据库操作代码保持简洁
- 业务代码不需要关心数据源切换
- 对于不同业务模块有清晰的包结构隔离
缺点:
- 配置较为复杂
- 跨模块查询需要在Service层手动处理
- 不同模块的事务无法统一管理,分布式事务支持有限
- 过多的EntityManagerFactory会增加内存开销
4.4 适用场景
- 已经使用ORM框架的项目
- 业务模块划分明确,数据模型相对独立
- 开发团队熟悉JPA或其他ORM框架
- 对分布式事务要求不高的场景
五、分库中间件策略
5.1 基本原理
利用专业的分库分表中间件(如ShardingSphere、MyCat等)进行垂直分片,通过中间件提供的路由和代理功能,实现对多个数据库的统一管理和访问。
这种方式将分片逻辑从应用层下沉到中间件层,简化了应用开发。
5.2 实现步骤(以ShardingSphere-JDBC为例)
5.2.1 添加依赖
xml
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.0</version>
</dependency>
5.2.2 配置垂直分片规则
less
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
# 产品表配置
products:
actual-data-nodes: product-db.products
product_categories:
actual-data-nodes: product-db.product_categories
props:
sql-show: true
5.2.3 创建实体类和Repository
less
@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 配置数据库访问
less
@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层使用
kotlin
@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 优缺点分析
优点:
- 分片逻辑下沉到中间件,对应用层透明
- 统一的事务管理,部分中间件支持弱XA事务
- 应用代码无需关心数据源切换
- 支持复杂的分片规则和读写分离
缺点:
- 引入额外的中间件,增加系统复杂度
- 对SQL有一定限制,部分复杂查询可能不支持
- 性能上会有一定的损耗
- 学习成本较高
5.4 适用场景
- 大型系统,数据量大且需要复杂分片策略
- 需要同时支持垂直分片和水平分片的场景
- 对分布式事务有一定要求的系统
六、微服务架构下的垂直分片策略
6.1 基本原理
在微服务架构中,每个微服务拥有自己独立的数据库,通过业务功能的拆分自然实现了垂直分片。
不同微服务之间通过API调用而非直接数据库访问进行交互,实现了数据的物理隔离和访问控制。
6.2 实现步骤
6.2.1 拆分微服务和数据库
根据业务领域划分微服务:
- 用户服务:管理用户相关数据
- 订单服务:管理订单相关数据
- 产品服务:管理产品相关数据
- ...
每个微服务拥有独立的数据库。
6.2.2 用户服务实现
less
// 用户服务数据库配置
@Configuration
@EnableJpaRepositories(basePackages = "com.example.userservice.repository")
public class UserServiceDbConfig {
// 数据源、事务管理器等配置
}
// 用户实体类
@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> {
// 查询方法
}
// 用户服务接口
@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 订单服务实现
less
// 订单服务数据库配置
@Configuration
@EnableJpaRepositories(basePackages = "com.example.orderservice.repository")
public class OrderServiceDbConfig {
// 数据源、事务管理器等配置
}
// 订单实体类
@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);
}
// 订单服务
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private UserClient userClient;
public Order createOrder(Order order) {
// 通过Feign调用用户服务验证用户
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 优缺点分析
优点:
- 业务和数据完全解耦,每个服务独立发展
- 技术栈可以灵活选择,不同服务可以使用不同的数据库类型
- 服务独立部署和扩展,提高系统弹性
- 服务边界清晰,符合领域驱动设计理念
缺点:
- 分布式事务处理复杂,通常需要采用最终一致性方案
- 跨服务查询需要通过API组合数据,可能影响性能
- 系统复杂度增加,运维成本提高
- 服务拆分需要充分考虑业务边界,拆分不当会导致频繁的跨服务调用
6.4 适用场景
- 大型复杂系统,需要独立扩展不同业务模块
- 团队组织结构与业务划分一致,适合采用微服务架构
- 长期演进的系统,需要技术栈灵活性
- 对服务隔离性和弹性有较高要求的场景
七、策略比较
策略 | 复杂度 | 透明度 | 事务支持 | 跨库查询 | 性能影响 | 维护成本 |
---|---|---|---|---|---|---|
多数据源配置 | 低 | 低 | 单库事务 | 需代码处理 | 低 | 低 |
动态数据源路由 | 中 | 中 | 单库事务 | 需代码处理 | 低 | 中 |
ORM多数据源 | 中 | 中 | 单库事务 | 需代码处理 | 低 | 中 |
分库中间件 | 高 | 高 | 分布式事务 | 部分支持 | 中 | 高 |
微服务架构 | 高 | 低 | 最终一致性 | API组合 | 中 | 高 |
注:分布式事务支持程度取决于所选中间件或自主实现的数据一致性保障机制
八、垂直分片的最佳实践
8.1 数据模型设计
- 按业务领域划分表
-
- 将相关性强的表分到同一个数据库
- 尽量避免跨库关联查询
- 使用冗余字段减少跨库依赖
- 合理使用主键
-
- 避免使用自增主键(特别是在未来可能需要水平分片的场景)
- 考虑使用UUID或分布式ID生成器
- 建立合理的索引降低查询压力
- 数据冗余与一致性平衡
-
- 适当冗余关键数据降低跨库查询
- 建立数据同步机制保证最终一致性
- 区分强一致性场景和最终一致性场景
8.2 事务处理
- 本地事务处理
-
- 尽量将事务限制在单一数据库内
- 单数据库事务使用Spring的@Transactional注解
- 分布式事务策略
-
- 对于简单场景:两阶段提交(XA)
- 对于高并发场景:TCC(Try-Confirm-Cancel)
- 对于长事务场景:Saga模式
- 考虑使用Seata等分布式事务框架
- 最终一致性实现
-
- 基于消息队列的事件驱动架构
- 补偿机制和重试策略
- 幂等设计确保操作可重复执行
8.3 查询优化
- 减少跨库查询
-
- 合理划分业务边界,减少跨业务查询需求
- 使用数据冗余避免频繁跨库查询
- 考虑使用CQRS模式,为查询场景构建专用视图
- 查询路由策略
-
- 分析查询模式,优化分片键选择
- 对于复杂查询,考虑使用查询视图或搜索引擎
- 合理使用缓存减少数据库访问
- 结果聚合处理
-
- 在应用层对多数据源结果进行聚合
- 使用并行查询提高效率
- 结果分页和懒加载减少数据传输量