SpringBoot與Druid整合,實現(xiàn)電商主從數(shù)據(jù)庫同步系統(tǒng)
作者:Java知識日歷
通過引入主從數(shù)據(jù)庫同步系統(tǒng),可以顯著提升電商平臺的性能和穩(wěn)定性,同時保證數(shù)據(jù)的一致性和安全性。Druid連接池也提供了強大的監(jiān)控和安全防護功能,使得整個系統(tǒng)更加健壯和可靠。
通過引入主從數(shù)據(jù)庫同步系統(tǒng),可以顯著提升電商平臺的性能和穩(wěn)定性,同時保證數(shù)據(jù)的一致性和安全性。Druid連接池也提供了強大的監(jiān)控和安全防護功能,使得整個系統(tǒng)更加健壯和可靠。
我們?yōu)槭裁催x擇Druid?
- 高效的連接管理:Druid 提供了高效的物理連接復(fù)用機制,能夠快速獲取和釋放數(shù)據(jù)庫連接,減少連接建立和關(guān)閉的開銷。
- 異步初始化:支持異步初始化連接池,提高應(yīng)用啟動速度。
- 詳細的統(tǒng)計信息:Druid 內(nèi)置了豐富的統(tǒng)計功能,可以收集 SQL 執(zhí)行情況、慢查詢記錄等詳細數(shù)據(jù),幫助我們更好地進行性能調(diào)優(yōu)。
- 防火墻規(guī)則:支持配置防火墻規(guī)則,限制哪些 IP 地址可以訪問數(shù)據(jù)庫,增強安全性。
- SQL 防注入:提供 WallFilter 插件,防止 SQL 注入攻擊,保護數(shù)據(jù)庫不受惡意 SQL 的影響。
- 多數(shù)據(jù)源支持:Druid 支持配置多個數(shù)據(jù)源,非常適合實現(xiàn)讀寫分離等高級場景。在我們的項目中,主從數(shù)據(jù)庫的配置正是利用了這一特性。
哪些公司在使用Druid?
- 阿里巴巴 :Druid 是由阿里巴巴開源的一個項目,最初是為了滿足其內(nèi)部龐大的業(yè)務(wù)需求而開發(fā)。目前用于阿里巴巴旗下的淘寶、天貓等電商平臺的數(shù)據(jù)庫連接管理。
- 京東 : 在京東的電商平臺中,Druid 用于處理高并發(fā)的讀寫請求,提升系統(tǒng)性能和穩(wěn)定性。
- 美團點評 : 在美團點評的各個業(yè)務(wù)線中,Druid 用于提高數(shù)據(jù)庫的響應(yīng)速度和吞吐量,特別是在高并發(fā)場景下的表現(xiàn)。
- 小米 : 在小米的電商平臺和其他業(yè)務(wù)系統(tǒng)中,Druid 用于高效地管理數(shù)據(jù)庫連接,確保系統(tǒng)的穩(wěn)定性和性能。
- 去哪兒網(wǎng) : 在去哪兒網(wǎng)的業(yè)務(wù)系統(tǒng)中,Druid 用于處理大量的用戶請求,提高數(shù)據(jù)庫的并發(fā)處理能力。
- 攜程旅行網(wǎng) : 在攜程的電商平臺中,Druid 用于管理和優(yōu)化數(shù)據(jù)庫連接,支持高并發(fā)的讀寫操作。
- 知乎 : 在知乎的系統(tǒng)中,Druid 用于管理和優(yōu)化數(shù)據(jù)庫連接,支持高并發(fā)的讀寫操作,確保系統(tǒng)的穩(wěn)定性和性能。
- 拼多多 : 在拼多多的系統(tǒng)中,Druid 用于管理和優(yōu)化數(shù)據(jù)庫連接,支持高并發(fā)的讀寫操作,確保系統(tǒng)的穩(wěn)定性和性能。
代碼實操
<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
動態(tài)數(shù)據(jù)源上下文持有者
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();
}
}
數(shù)據(jù)源配置
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);
}
}
測試
添加產(chǎn)品
curl -X POST http://localhost:8080/products/ \
-H "Content-Type: application/json" \
-d '{"name": "Sample Product", "price": 99.99}'
Respons
true
獲取產(chǎn)品
curl -X GET http://localhost:8080/products/1
Respons
{"id":1,"name":"Sample Product","price":99.99}
責(zé)任編輯:武曉燕
來源:
Java知識日歷