Java+MySQL实现的分布式网上商城系统完整源码解析
基于Java+MySQL的分布式网上商城系统架构设计与实现
作者:[智能助手]
最近更新:2023年10月15日
随着电子商务的蓬勃发展,构建高可用、可扩展的分布式网上商城系统成为企业数字化转型的关键。本文将深入解析基于Java+MySQL实现的分布式网上商城系统的完整架构设计和核心代码实现。
1. 系统架构设计
1.1 整体架构概览
本系统采用微服务架构风格,将单体应用拆分为多个松耦合的服务。整体架构如下:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 客户端层 │ │ 网关层 │ │ 微服务层 │
│ │ │ │ │ │
│ - Web前端 │───▶│ - Spring Cloud │───▶│ - 用户服务 │
│ - 移动端 │ │ Gateway │ │ - 商品服务 │
│ - 小程序 │ │ - 认证/鉴权 │ │ - 订单服务 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ 公共组件层 │
│ │
│ - 配置中心 │
│ - 注册中心 │
│ - 监控中心 │
└─────────────────┘
│
▼
┌─────────────────┐
│ 数据层 │
│ │
│ - MySQL集群 │
│ - Redis集群 │
│ - Elasticsearch │
└─────────────────┘
1.2 技术栈选型
- 后端框架: Spring Boot 3.x + Spring Cloud 2022.x
- 数据库: MySQL 8.0(分库分表)
- 缓存: Redis 7.0(集群模式)
- 服务注册与发现: Nacos 2.2.x
- 配置中心: Nacos
- API网关: Spring Cloud Gateway
- 分布式事务: Seata
- 消息队列: RocketMQ 5.0
- 搜索引擎: Elasticsearch 8.0
- 监控: Prometheus + Grafana
2. 数据库设计
2.1 核心表结构设计
``sqluser
-- 用户表(分表键:user_id)
CREATE TABLE(user_idbigint(20) NOT NULL COMMENT '用户ID',usernamevarchar(50) NOT NULL COMMENT '用户名',passwordvarchar(100) NOT NULL COMMENT '密码',emailvarchar(100) DEFAULT NULL COMMENT '邮箱',phonevarchar(20) DEFAULT NULL COMMENT '手机号',statustinyint(1) DEFAULT '1' COMMENT '状态:0-禁用,1-正常',create_timedatetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',update_timedatetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',user_id
PRIMARY KEY (),uk_username
UNIQUE KEY(username),idx_email
KEY(email),idx_phone
KEY(phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- 商品表
CREATE TABLE product (
product_id bigint(20) NOT NULL COMMENT '商品ID',
product_name varchar(200) NOT NULL COMMENT '商品名称',
category_id bigint(20) NOT NULL COMMENT '分类ID',
price decimal(10,2) NOT NULL COMMENT '价格',
stock int(11) NOT NULL COMMENT '库存',
description text COMMENT '商品描述',
status tinyint(1) DEFAULT '1' COMMENT '状态:0-下架,1-上架',
create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (product_id),
KEY idx_category (category_id),
KEY idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';
-- 订单表(分表键:order_id)
CREATE TABLE order (
order_id bigint(20) NOT NULL COMMENT '订单ID',
user_id bigint(20) NOT NULL COMMENT '用户ID',
total_amount decimal(10,2) NOT NULL COMMENT '订单总金额',
status tinyint(1) NOT NULL COMMENT '订单状态:0-待支付,1-已支付,2-已发货,3-已完成,4-已取消',
payment_time datetime DEFAULT NULL COMMENT '支付时间',
create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (order_id),
KEY idx_user_id (user_id),
KEY idx_status (status),
KEY idx_create_time (create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
-- 订单项表
CREATE TABLE order_item (
id bigint(20) NOT NULL COMMENT '主键ID',
order_id bigint(20) NOT NULL COMMENT '订单ID',
product_id bigint(20) NOT NULL COMMENT '商品ID',
product_name varchar(200) NOT NULL COMMENT '商品名称',
quantity int(11) NOT NULL COMMENT '购买数量',
price decimal(10,2) NOT NULL COMMENT '商品单价',
total_price decimal(10,2) NOT NULL COMMENT '商品总价',
PRIMARY KEY (id),
KEY idx_order_id (order_id),
KEY idx_product_id (product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单项表';
```
3. 微服务核心实现
3.1 用户服务实现
用户服务Controller:
```java
@RestController
@RequestMapping("/api/user")
@Validated
public class UserController {
@Autowired
private UserService userService;
/
用户注册
/
@PostMapping("/register")
public Result<UserVO> register(@Valid @RequestBody UserRegisterDTO registerDTO) {
UserVO userVO = userService.register(registerDTO);
return Result.success(userVO);
}
/
用户登录
/
@PostMapping("/login")
public Result<LoginVO> login(@Valid @RequestBody UserLoginDTO loginDTO) {
LoginVO loginVO = userService.login(loginDTO);
return Result.success(loginVO);
}
/
获取用户信息
/
@GetMapping("/info/{userId}")
public Result<UserVO> getUserInfo(@PathVariable Long userId) {
UserVO userVO = userService.getUserInfo(userId);
return Result.success(userVO);
}
}
/
用户服务实现
/
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserVO register(UserRegisterDTO registerDTO) {
// 校验用户名是否已存在
if (userMapper.existsByUsername(registerDTO.getUsername())) {
throw new BusinessException("用户名已存在");
}
// 创建用户
User user = new User();
user.setUserId(SnowflakeIdGenerator.nextId());
user.setUsername(registerDTO.getUsername());
user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));
user.setEmail(registerDTO.getEmail());
user.setPhone(registerDTO.getPhone());
user.setStatus(1);
userMapper.insert(user);
// 返回用户信息
return UserConvert.INSTANCE.toVO(user);
}
@Override
public LoginVO login(UserLoginDTO loginDTO) {
// 根据用户名查询用户
User user = userMapper.selectByUsername(loginDTO.getUsername());
if (user == null) {
throw new BusinessException("用户名或密码错误");
}
// 验证密码
if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
throw new BusinessException("用户名或密码错误");
}
// 生成JWT token
String token = JwtUtil.generateToken(user.getUserId(), user.getUsername());
// 将用户信息存入Redis
String redisKey = "user:token:" + token;
redisTemplate.opsForValue().set(redisKey, user, Duration.ofHours(2));
LoginVO loginVO = new LoginVO();
loginVO.setToken(token);
loginVO.setUserInfo(UserConvert.INSTANCE.toVO(user));
return loginVO;
}
}
```
3.2 商品服务实现
商品搜索实现:
```java
@Service
@Slf4j
public class ProductSearchServiceImpl implements ProductSearchService {
@Autowired
private ElasticsearchRestTemplate elasticsearchTemplate;
@Autowired
private ProductMapper productMapper;
/
商品搜索
/
@Override
public PageResult<ProductVO> searchProducts(ProductSearchDTO searchDTO) {
// 构建搜索条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 关键词搜索
if (StringUtils.hasText(searchDTO.getKeyword())) {
MultiMatchQueryBuilder multiMatchQuery = QueryBuilders.multiMatchQuery(searchDTO.getKeyword(),
"productName", "description", "categoryName");
queryBuilder.withQuery(multiMatchQuery);
} else {
queryBuilder.withQuery(QueryBuilders.matchAllQuery());
}
// 分类过滤
if (searchDTO.getCategoryId() != null) {
queryBuilder.withFilter(QueryBuilders.termQuery("categoryId", searchDTO.getCategoryId()));
}
// 价格区间过滤
if (searchDTO.getMinPrice() != null || searchDTO.getMaxPrice() != null) {
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
if (searchDTO.getMinPrice() != null) {
rangeQuery.gte(searchDTO.getMinPrice());
}
if (searchDTO.getMaxPrice() != null) {
rangeQuery.lte(searchDTO.getMaxPrice());
}
queryBuilder.withFilter(rangeQuery);
}
// 分页
queryBuilder.withPageable(PageRequest.of(searchDTO.getPage() - 1, searchDTO.getSize()));
// 排序
if (StringUtils.hasText(searchDTO.getSortBy())) {
Sort sort = "price".equals(searchDTO.getSortBy()) ?
Sort.by(Sort.Direction.fromString(searchDTO.getSortOrder()), "price") :
Sort.by(Sort.Direction.DESC, "createTime");
queryBuilder.withSort(sort);
}
NativeSearchQuery searchQuery = queryBuilder.build();
// 执行搜索
SearchHits<ProductDocument> searchHits = elasticsearchTemplate.search(searchQuery, ProductDocument.class);
// 转换为分页结果
List<ProductVO> productList = searchHits.getSearchHits().stream()
.map(hit -> {
ProductDocument document = hit.getContent();
return ProductConvert.INSTANCE.documentToVO(document);
})
.collect(Collectors.toList());
long total = searchHits.getTotalHits();
return new PageResult<>(productList, total, searchDTO.getPage(), searchDTO.getSize());
}
/
同步商品数据到ES
/
@Override
@Async
public void syncProductsToES() {
try {
// 分页查询所有商品
int page = 1;
int size = 1000;
List<ProductDocument> documents = new ArrayList<>();
while (true) {
PageHelper.startPage(page, size);
List<Product> products = productMapper.selectAll();
if (products.isEmpty()) {
break;
}
// 转换为文档对象
List<ProductDocument> pageDocuments = products.stream()
.map(ProductConvert.INSTANCE::toDocument)
.collect(Collectors.toList());
documents.addAll(pageDocuments);
if (products.size() < size) {
break;
}
page++;
}
// 批量保存到ES
if (!documents.isEmpty()) {
elasticsearchTemplate.save(documents);
log.info("成功同步 {} 个商品到ES", documents.size());
}
} catch (Exception e) {
log.error("同步商品数据到ES失败", e);
}
}
}
```
3.3 订单服务实现
下单业务实现(使用分布式事务):
```java
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Autowired
private ProductService productService;
@Autowired
private RocketMQTemplate rocketMQTemplate;
/
创建订单(使用Seata分布式事务)
/
@Override
@GlobalTransactional(rollbackFor = Exception.class)
public OrderVO createOrder(CreateOrderDTO createOrderDTO) {
Long userId = createOrderDTO.getUserId();
List<OrderItemDTO> items = createOrderDTO.getItems();
// 1. 验证商品库存
checkStock(items);
// 2. 生成订单ID
Long orderId = SnowflakeIdGenerator.nextId();
// 3. 计算订单总金额
BigDecimal totalAmount = calculateTotalAmount(items);
// 4. 创建订单
Order order = new Order();
order.setOrderId(orderId);
order.setUserId(userId);
order.setTotalAmount(totalAmount);
order.setStatus(OrderStatus.WAITING_PAYMENT.getCode());
orderMapper.insert(order);
// 5. 创建订单项
createOrderItems(orderId, items);
// 6. 扣减库存
deductStock(items);
// 7. 发送订单创建消息
sendOrderCreatedMessage(orderId);
// 8. 返回订单信息
return getOrderDetail(orderId);
}
/
验证库存
/
private void checkStock(List<OrderItemDTO> items) {
for (OrderItemDTO item : items) {
ProductStockDTO stockDTO = productService.getProductStock(item.getProductId());
if (stockDTO.getStock() < item.getQuantity()) {
throw new BusinessException("商品【" + stockDTO.getProductName() + "】库存不足");
}
}
}
/
计算订单总金额
/
private BigDecimal calculateTotalAmount(List<OrderItemDTO> items) {
return items.stream()
.map(item -> {
ProductPriceDTO priceDTO = productService.getProductPrice(item.getProductId());
return priceDTO.getPrice().multiply(new BigDecimal(item.getQuantity()));
})
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
/
创建订单项
/
private void createOrderItems(Long orderId, List<OrderItemDTO> items) {
for (OrderItemDTO item : items) {
OrderItem orderItem = new OrderItem();
orderItem.setId(SnowflakeIdGenerator.nextId());
orderItem.setOrderId(orderId);
orderItem.setProductId(item.getProductId());
ProductDetailDTO productDetail = productService.getProductDetail(item.getProductId());
orderItem.setProductName(productDetail.getProductName());
orderItem.setQuantity(item.getQuantity());
orderItem.setPrice(productDetail.getPrice());
orderItem.setTotalPrice(productDetail.getPrice().multiply(new BigDecimal(item.getQuantity())));
orderItemMapper.insert(orderItem);
}
}
/
扣减库存
/
private void deductStock(List<OrderItemDTO> items) {
List<ProductStockDeductDTO> deductList = items.stream()
.map(item -> {
ProductStockDeductDTO deductDTO = new ProductStockDeductDTO();
deductDTO.setProductId(item.getProductId());
deductDTO.setQuantity(item.getQuantity());
return deductDTO;
})
.collect(Collectors.toList());
productService.deductStock(deductList);
}
/
发送订单创建消息
/
private void sendOrderCreatedMessage(Long orderId) {
OrderCreatedMessage message = new OrderCreatedMessage();
message.setOrderId(orderId);
message.setCreateTime(new Date());
rocketMQTemplate.convertAndSend("ORDER_CREATED_TOPIC", message);
}
}
```
4. 分布式事务处理
4.1 Seata分布式事务配置
```yaml
application-seata.yml
seata:
enabled: true
application-id: order-service
tx-service-group: my_tx_group
enable-auto-data-source-proxy: true
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
group: SEATA_GROUP
username: nacos
password: nacos
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
namespace: ""
group: SEATA_GROUP
username: nacos
password: nacos
```
4.2 TCC模式实现库存扣减
```java
public interface ProductStockTccService {
/
Try阶段:预扣减库存
/
@Transactional
boolean deductStockPrepare(DeductStockDTO deductStockDTO);
/
Confirm阶段:确认扣减库存
/
boolean deductStockCommit(DeductStockDTO deductStockDTO);
/
Cancel阶段:回滚库存扣减
/
boolean deductStockRollback(DeductStockDTO deductStockDTO);
}
@Service
@Slf4j
public class ProductStockTccServiceImpl implements ProductStockTccService {
@Autowired
private ProductMapper productMapper;
@Override
@Transactional
public boolean deductStockPrepare(DeductStockDTO deductStockDTO) {
log.info("预扣减库存开始,商品ID:{},数量:{}",
deductStockDTO.getProductId(), deductStockDTO.getQuantity());
// 检查库存是否充足
Product product = productMapper.selectForUpdate(deductStockDTO.getProductId());
if (product.getStock() < deductStockDTO.getQuantity()) {
throw new BusinessException("库存不足");
}
// 预扣减库存(冻结库存)
int affectedRows = productMapper.freezeStock(
deductStockDTO.getProductId(),
deductStockDTO.getQuantity()
);
if (affectedRows == 0) {
throw new BusinessException("预扣减库存失败");
}
log.info("预扣减库存成功");
return true;
}
@Override
public boolean deductStockCommit(DeductStockDTO deductStockDTO) {
log.info("确认扣减库存开始,商品ID:{},数量:{}",
deductStockDTO.getProductId(), deductStockDTO.getQuantity());
// 实际扣减库存,释放冻结的库存
int affectedRows = productMapper.deductStock(deductStockDTO.getProductId(), deductStockDTO.getQuantity());
if (affectedRows == 0) {
log.error("确认扣减库存失败");
return false;
}
log.info("确认扣减库存成功");
return true;
}
@Override
public boolean deductStockRollback(DeductStockDTO deductStockDTO) {
log.info("回滚库存扣减开始,商品ID:{},数量:{}",
deductStockDTO.getProductId(), deductStockDTO.getQuantity());
// 回滚:释放冻结的库存
int affectedRows = productMapper.unfreezeStock(deductStockDTO.getProductId(), deductStockDTO.getQuantity());
if (affectedRows == 0) {
log.error("回滚库存扣减失败");
return false;
}
log.info("回滚库存扣减成功");
return true;
}
}
```
5. 系统优化实践
5.1 缓存优化
```java
@Service
@Slf4j
public class ProductCacheServiceImpl implements ProductCacheService {
@Autowired
private ProductMapper productMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String PRODUCT_CACHE_KEY = "product:info:";
private static final Duration CACHE_EXPIRE = Duration.ofMinutes(30);
/
获取商品信息(带缓存)
/
@Override
public ProductVO getProductWithCache(Long productId) {
String cacheKey = PRODUCT_CACHE_KEY + productId;
// 从缓存获取
ProductVO cachedProduct = (ProductVO) redisTemplate.opsForValue().get(cacheKey);
if (cachedProduct != null) {
return cachedProduct;
}
// 缓存不存在,从数据库查询
Product product = productMapper.selectById(productId);
if (product == null) {
return null;
}
ProductVO productVO = ProductConvert.INSTANCE.toVO(product);
// 写入缓存
redisTemplate.opsForValue().set(cacheKey, productVO, CACHE_EXPIRE);
return productVO;
}
/
更新商品缓存
/
@Override
public void updateProductCache(Product product) {
String cacheKey = PRODUCT_CACHE_KEY + product.getProductId();
ProductVO productVO = ProductConvert.INSTANCE.toVO(product);
redisTemplate.opsForValue().set(cacheKey, productVO, CACHE_EXPIRE);
}
/
删除商品缓存
/
@Override
public void deleteProductCache(Long productId) {
String cacheKey = PRODUCT_CACHE_KEY + productId;
redisTemplate.delete(cacheKey);
}
}
```
5.2 数据库分库分表配置
```yaml
application-sharding.yml
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/mall0?useUnicode=true&characterEncoding=utf8
username: root
password: 123456
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/mall1?useUnicode=true&characterEncoding=utf8
username: root
password: 123456
rules:
sharding:
tables:
user:
actual-data-nodes: ds$->{0..1}.user_$->{0..3}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: user-db-algorithm
table-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: user-table-algorithm
key-generate-strategy:
column: user_id
key-generator-name: snowflake
order:
actual-data-nodes: ds$->{0..1}.order_$->{0..7}
database-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order-db-algorithm
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order-table-algorithm
key-generate-strategy:
column: order_id
key-generator-name: snowflake
sharding-algorithms:
user-db-algorithm:
type: INLINE
props:
algorithm-expression: ds$->{user_id % 2}
user-table-algorithm:
type: INLINE
props:
algorithm-expression: user_$->{user_id % 4}
order-db-algorithm:
type: INLINE
props:
algorithm-expression: ds$->{order_id % 2}
order-table-algorithm:
type: INLINE
props:
algorithm-expression: order_$->{order_id % 8}
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 123
```
6. 系统部署与监控
6.1 Docker容器化部署
```dockerfile
商品服务Dockerfile
FROM openjdk:17-jdk-slim
VOLUME /tmp
ARG JAR_FILE=target/product-service-1.0.0.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
```
```yaml
docker-compose.yml
version: '3.8'
services:
注册中心
nacos:
image: nacos/nacos-server:v2.2.3
container_name: nacos
environment:
- MODE=standalone
ports:
- "8848:8848"
networks:
- mall-network
MySQL
mysql-master:
image: mysql:8.0
container_name: mysql-master
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: mall
ports:
- "3306:3306"
networks:
- mall-network
Redis
redis:
image: redis:7.0-alpine
container_name: redis
ports:
- "6379:6379"
networks:
- mall-network
商品服务
product-service:
build: ./product-service
container_name: product-service
environment:
- SPRING_PROFILES_ACTIVE=docker
depends_on:
- nacos
- mysql-master
- redis
networks:
- mall-network
deploy:
replicas: 2
networks:
mall-network:
driver: bridge
```
6.2 监控配置
```yaml
Prometheus配置
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'product-service'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['product-service:8080']
relabel_configs:
- source_labels: [address]
target_label: instance
regex: '(.):.'
replacement: '${1}'
- job_name: 'order-service'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['order-service:8080']
```
7. 总结
本文详细介绍了基于Java+MySQL的分布式网上商城系统的完整实现方案。系统采用微服务架构,通过Spring Cloud生态组件实现服务治理,使用MySQL分库分表支撑海量数据存储,借助Redis提升系统性能,利用Elasticsearch实现商品搜索,通过Seata保证分布式事务一致性。
系统具有以下特点:
- 高可用性:通过微服务架构和集群部署,保证系统的高可用
- 可扩展性:服务无状态设计,支持水平扩展
- 高性能:多级缓存、数据库优化、异步处理等提升系统性能
- 可维护性:清晰的架构分层和模块划分,便于维护和迭代
在实际项目开发中,还需要根据具体业务需求进行适当的调整和优化。希望本文能为正在开发分布式电商系统的开发者提供有价值的参考。
版权声明:本文为CSDN博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。
更多推荐


所有评论(0)