订单创建和库存扣减操作必须保证原子性,避免出现超卖或欠卖的情况,利用 ShardingSphere 的分布式事务管理功能,确保跨多个数据库的操作具有一致性。
为什么选择ShardingSphere?
XA 事务支持:ShardingSphere 提供了 XA 协议的支持,确保跨多个数据库的操作具有强一致性,即使在分布式环境下也能保持数据的一致性。
柔性事务:除了 XA 事务外,ShardingSphere 还支持柔性事务解决方案,如 Saga 和 BASE 模型,以适应不同的业务场景。
动态添加/删除数据源:可以在运行时动态地添加或删除数据源,而无需重启应用程序,提高了系统的灵活性和可维护性。
SQL 解析引擎:ShardingSphere 内置了强大的 SQL 解析引擎,能够解析和改写 SQL 语句,使其适用于分片后的数据库结构。
透明化访问:通过标准的 JDBC 接口访问分片后的数据库,无需关心底层的数据分布情况。
多种部署模式:ShardingSphere 支持多种部署模式,包括代理模式和嵌入式模式。
容器化支持:支持 Docker 和 Kubernetes 等容器化平台,便于自动化部署和管理。
简化开发:通过 Spring 的事务注解(如
@Transactional),开发者可以轻松地进行事务管理,无需手动编写复杂的事务逻辑。集成 Atomikos:Atomikos 是一个成熟的事务管理器,与 ShardingSphere 结合使用可以提供可靠的分布式事务管理能力。
灵活的分片策略:可以根据不同的业务需求选择合适的分片算法(如
INLINE、RANGE等),确保数据均匀分布并优化查询性能。支持水平分片:ShardingSphere 支持将数据按一定的规则分散到多个数据库实例中,从而实现水平扩展。这对于处理大规模订单和产品数据非常有效。
代码实操
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Lombok (optional) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- ShardingSphere JDBC Core -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.2</version>
</dependency>
<!-- Atomikos Transaction Manager -->
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jta</artifactId>
<version>5.0.13</version>
</dependency>application.yml
server:
port:8080
spring:
application:
name:sharding-demo
# 数据库配置
spring:
shardingsphere:
datasource:
names:ds0,ds1
ds0:
type:com.zaxxer.hikari.HikariDataSource
driver-class-name:com.mysql.cj.jdbc.Driver
jdbc-url:jdbc:mysql://localhost:3306/db0?useSSL=false&serverTimezone=UTC
username:root
password:root
ds1:
type:com.zaxxer.hikari.HikariDataSource
driver-class-name:com.mysql.cj.jdbc.Driver
jdbc-url:jdbc:mysql://localhost:3306/db1?useSSL=false&serverTimezone=UTC
username:root
password:root
rules:
sharding:
tables:
t_product:
actual-data-nodes:ds$->{0..1}.t_product_$->{0..1}
table-strategy:
standard:
sharding-column:product_id
sharding-algorithm-name:t_product_inline
key-generate-strategy:
column:product_id
key-generator-name:snowflake
t_order:
actual-data-nodes:ds$->{0..1}.t_order_$->{0..1}
table-strategy:
standard:
sharding-column:order_id
sharding-algorithm-name:t_order_inline
key-generate-strategy:
column:order_id
key-generator-name:snowflake
binding-tables:
-t_product,t_order
sharding-algorithms:
t_product_inline:
type:INLINE
props:
algorithm-expression:t_product_$->{product_id%2}
t_order_inline:
type:INLINE
props:
algorithm-expression:t_order_$->{order_id%2}
key-generators:
snowflake:
type:SNOWFLAKE
props:
sql-show:trueProduct
package com.example.demo.entity;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Data
@Entity(name = "t_product")
publicclass Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long productId;
private String productName;
private Integer stock;
}Order
package com.example.demo.entity;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Data
@Entity(name = "t_order")
publicclass Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long orderId;
private Long productId;
private Integer quantity;
}Repository
package com.example.demo.repository;
import com.example.demo.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProductRepository extends JpaRepository<Product, Long> {
}OrderRepository
package com.example.demo.repository;
import com.example.demo.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
public interface OrderRepository extends JpaRepository<Order, Long> {
}Service
package com.example.demo.service;
import com.example.demo.entity.Product;
import com.example.demo.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
publicclass ProductService {
@Autowired
private ProductRepository productRepository;
public Product getProductById(Long productId) {
return productRepository.findById(productId).orElseThrow(() -> new RuntimeException("Product not found"));
}
public void updateStock(Long productId, int quantity) {
Product product = getProductById(productId);
product.setStock(product.getStock() - quantity);
productRepository.save(product);
}
}OrderService
package com.example.demo.service;
import com.example.demo.entity.Order;
import com.example.demo.repository.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
publicclass OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private ProductService productService;
@Transactional
public void placeOrder(Long productId, int quantity) {
// 检查库存
if (!productService.getProductById(productId).getStock().equals(quantity)) {
thrownew RuntimeException("Insufficient stock");
}
// 创建订单
Order order = new Order();
order.setProductId(productId);
order.setQuantity(quantity);
orderRepository.save(order);
// 扣减库存
productService.updateStock(productId, quantity);
}
}Controller
package com.example.demo.controller;
import com.example.demo.entity.Product;
import com.example.demo.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/products")
publicclass ProductController {
@Autowired
private ProductService productService;
@GetMapping("/{productId}")
public Product getProduct(@PathVariable Long productId) {
return productService.getProductById(productId);
}
@PostMapping("/")
public Product createProduct(@RequestBody Product product) {
return productService.createProduct(product);
}
}OrderController
package com.example.demo.controller;
import com.example.demo.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/orders")
publicclass OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/place-order")
public String placeOrder(@RequestParam Long productId, @RequestParam int quantity) {
try {
orderService.placeOrder(productId, quantity);
return"Order placed successfully";
} catch (Exception e) {
return"Failed to place order: " + e.getMessage();
}
}
}Application
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}测试
curl -X POST http://localhost:8080/orders/place-order \
-d "productId=1&quantity=5"Respons
Order placed successfully关注我,送Java福利
/**
* 这段代码只有Java开发者才能看得懂!
* 关注我微信公众号之后,
* 发送:"666",
* 即可获得一本由Java大神一手面试经验诚意出品
* 《Java开发者面试百宝书》Pdf电子书
* 福利截止日期为2025年06月28日止
* 手快有手慢没!!!
*/
System.out.println("请关注我的微信公众号:");
System.out.println("Java知识日历");
2000

被折叠的 条评论
为什么被折叠?



