SpringBoot與Druid整合,實現電商主從數據庫同步系統
作者:Java知識日歷
通過引入主從數據庫同步系統,可以顯著提升電商平臺的性能和穩定性,同時保證數據的一致性和安全性。Druid連接池也提供了強大的監控和安全防護功能,使得整個系統更加健壯和可靠。
通過引入主從數據庫同步系統,可以顯著提升電商平臺的性能和穩定性,同時保證數據的一致性和安全性。Druid連接池也提供了強大的監控和安全防護功能,使得整個系統更加健壯和可靠。
我們為什么選擇Druid?
- 高效的連接管理:Druid 提供了高效的物理連接復用機制,能夠快速獲取和釋放數據庫連接,減少連接建立和關閉的開銷。
- 異步初始化:支持異步初始化連接池,提高應用啟動速度。
- 詳細的統計信息:Druid 內置了豐富的統計功能,可以收集 SQL 執行情況、慢查詢記錄等詳細數據,幫助我們更好地進行性能調優。
- 防火墻規則:支持配置防火墻規則,限制哪些 IP 地址可以訪問數據庫,增強安全性。
- SQL 防注入:提供 WallFilter 插件,防止 SQL 注入攻擊,保護數據庫不受惡意 SQL 的影響。
- 多數據源支持:Druid 支持配置多個數據源,非常適合實現讀寫分離等高級場景。在我們的項目中,主從數據庫的配置正是利用了這一特性。
哪些公司在使用Druid?
- 阿里巴巴 :Druid 是由阿里巴巴開源的一個項目,最初是為了滿足其內部龐大的業務需求而開發。目前用于阿里巴巴旗下的淘寶、天貓等電商平臺的數據庫連接管理。
- 京東 : 在京東的電商平臺中,Druid 用于處理高并發的讀寫請求,提升系統性能和穩定性。
- 美團點評 : 在美團點評的各個業務線中,Druid 用于提高數據庫的響應速度和吞吐量,特別是在高并發場景下的表現。
- 小米 : 在小米的電商平臺和其他業務系統中,Druid 用于高效地管理數據庫連接,確保系統的穩定性和性能。
- 去哪兒網 : 在去哪兒網的業務系統中,Druid 用于處理大量的用戶請求,提高數據庫的并發處理能力。
- 攜程旅行網 : 在攜程的電商平臺中,Druid 用于管理和優化數據庫連接,支持高并發的讀寫操作。
- 知乎 : 在知乎的系統中,Druid 用于管理和優化數據庫連接,支持高并發的讀寫操作,確保系統的穩定性和性能。
- 拼多多 : 在拼多多的系統中,Druid 用于管理和優化數據庫連接,支持高并發的讀寫操作,確保系統的穩定性和性能。
代碼實操
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
application.yml
server:
port:8080
spring:
datasource:
type:com.alibaba.druid.pool.DruidDataSource
druid:
master:
url:jdbc:mysql://localhost:3306/master_db?useSSL=false&serverTimezone=UTC
username:root
password:root
slave:
url:jdbc:mysql://localhost:3307/slave_db?useSSL=false&serverTimezone=UTC
username:root
password:root
mybatis-plus:
configuration:
log-impl:org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations:classpath*:mybatis-mapper.xml
動態數據源上下文持有者
package com.example.demo.config;
publicclass DynamicDataSourceContextHolder {
privatestaticfinal 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();
}
}
數據源配置
package com.example.demo.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
publicclass DataSourceConfig {
@Value("${spring.datasource.druid.master.url}")
private String masterUrl;
@Value("${spring.datasource.druid.master.username}")
private String masterUsername;
@Value("${spring.datasource.druid.master.password}")
private String masterPassword;
@Value("${spring.datasource.druid.slave.url}")
private String slaveUrl;
@Value("${spring.datasource.druid.slave.username}")
private String slaveUsername;
@Value("${spring.datasource.druid.slave.password}")
private String slavePassword;
@Bean
public DataSource dynamicDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource());
targetDataSources.put("slave", slaveDataSource());
AbstractRoutingDataSource abstractRoutingDataSource = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
};
abstractRoutingDataSource.setDefaultTargetDataSource(masterDataSource());
abstractRoutingDataSource.setTargetDataSources(targetDataSources);
return abstractRoutingDataSource;
}
@Bean
public DataSource masterDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(masterUrl);
dataSource.setUsername(masterUsername);
dataSource.setPassword(masterPassword);
return dataSource;
}
@Bean
public DataSource slaveDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(slaveUrl);
dataSource.setUsername(slaveUsername);
dataSource.setPassword(slavePassword);
return dataSource;
}
}
實體類
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("product")
publicclass Product {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Double price;
// getters and setters
}
Mapper接口
package com.example.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.Product;
public interface ProductMapper extends BaseMapper<Product> {
}
mybatis-mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.ProductMapper">
<select id="selectById" resultType="com.example.demo.entity.Product">
SELECT * FROM product WHERE id = #{id}
</select>
<insert id="insert" parameterType="com.example.demo.entity.Product">
INSERT INTO product (name, price) VALUES (#{name}, #{price})
</insert>
</mapper>
Service接口
package com.example.demo.service;
import com.example.demo.entity.Product;
public interface ProductService {
Product getProductById(Long id);
boolean saveProduct(Product product);
}
Service
package com.example.demo.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.config.DynamicDataSourceContextHolder;
import com.example.demo.entity.Product;
import com.example.demo.mapper.ProductMapper;
import com.example.demo.service.ProductService;
import org.springframework.stereotype.Service;
@Service
publicclass ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
@Override
public Product getProductById(Long id) {
DynamicDataSourceContextHolder.setDataSourceType("slave");
try {
return getById(id);
} finally {
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
@Override
public boolean saveProduct(Product product) {
DynamicDataSourceContextHolder.setDataSourceType("master");
try {
return save(product);
} finally {
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
}
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("/{id}")
public Product getProduct(@PathVariable Long id) {
return productService.getProductById(id);
}
@PostMapping("/")
public boolean addProduct(@RequestBody Product product) {
return productService.saveProduct(product);
}
}
Application
package com.example.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
測試
添加產品
curl -X POST http://localhost:8080/products/ \
-H "Content-Type: application/json" \
-d '{"name": "Sample Product", "price": 99.99}'
Respons
true
獲取產品
curl -X GET http://localhost:8080/products/1
Respons
{"id":1,"name":"Sample Product","price":99.99}
責任編輯:武曉燕
來源:
Java知識日歷