通过引入主从数据库同步系统,可以显著提升电商平台的性能和稳定性,同时保证数据的一致性和安全性。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福利
/**
* 这段代码只有Java开发者才能看得懂!
* 关注我微信公众号之后,
* 发送:"666",
* 即可获得一本由Java大神一手面试经验诚意出品
* 《Java开发者面试百宝书》Pdf电子书
* 福利截止日期为2025年02月28日止
* 手快有手慢没!!!
*/
System.out.println("请关注我的微信公众号:");
System.out.println("Java知识日历");