处理高并发写入问题需要综合考虑系统的架构设计、数据库优化、缓存机制、异步处理等多个方面。以下是详细的解决方案和相应的代码示例。
一. 数据库层优化
1. 批量插入
批量插入可以减少数据库连接的开销,提高写入效率。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class BatchInsertExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/my_database";
String user = "root";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
String insertSQL = "INSERT INTO my_table (column1, column2) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {
conn.setAutoCommit(false);
for (int i = 0; i < 1000; i++) {
pstmt.setString(1, "value" + i);
pstmt.setString(2, "value" + i);
pstmt.addBatch();
}
pstmt.executeBatch();
conn.commit();
System.out.println("Batch insert completed successfully.");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2. 分区表
将大表分成多个小表,减少单个表的写入压力。
CREATE TABLE my_partitioned_table (
id INT,
name VARCHAR(100),
created_at DATE
) PARTITION BY RANGE (YEAR(created_at)) (
PARTITION p0 VALUES LESS THAN (2020),
PARTITION p1 VALUES LESS THAN (2021),
PARTITION p2 VALUES LESS THAN (2022),
PARTITION p3 VALUES LESS THAN (2023)
);
二. 缓存机制
1. 写入缓冲(Write-behind Cache)
通过在内存中缓存写操作,减少直接数据库写入压力。
import redis.clients.jedis.Jedis;
public class RedisWriteBehindCache {
private static final String REDIS_HOST = "localhost";
private static final int REDIS_PORT = 6379;
public static void main(String[] args) {
try (Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT)) {
for (int i = 0; i < 1000; i++) {
String key = "key" + i;
String value = "value" + i;
jedis.set(key, value);
}
System.out.println("Data written to Redis successfully.");
}
}
}
三. 分布式系统设计
1. 分库分表
将数据水平切分到多个库或表中,减轻单个库或表的负载。
CREATE TABLE my_table_0 (
id INT PRIMARY KEY,
column1 VARCHAR(255),
column2 VARCHAR(255)
);
CREATE TABLE my_table_1 (
id INT PRIMARY KEY,
column1 VARCHAR(255),
column2 VARCHAR(255)
);
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class ShardingExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/my_database";
String user = "root";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
conn.setAutoCommit(false);
for (int i = 0; i < 1000; i++) {
String tableName = (i % 2 == 0) ? "my_table_0" : "my_table_1";
String insertSQL = "INSERT INTO " + tableName + " (id, column1, column2) VALUES (?, ?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {
pstmt.setInt(1, i);
pstmt.setString(2, "value" + i);
pstmt.setString(3, "value" + i);
pstmt.executeUpdate();
}
}
conn.commit();
System.out.println("Data inserted into sharded tables successfully.");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
四. 异步处理
1. 使用消息队列
通过消息队列(如Kafka、RabbitMQ)进行异步写入,削峰填谷。
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class KafkaProducerExample {
public static void main(String[] args) {
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
for (int i = 0; i < 1000; i++) {
String key = "key" + i;
String value = "value" + i;
producer.send(new ProducerRecord<>("my_topic", key, value));
}
producer.close();
System.out.println("Data sent to Kafka successfully.");
}
}
五. 数据库连接池
使用数据库连接池(如HikariCP、C3P0)来管理数据库连接,提高并发处理能力。
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class HikariCPExample {
private static HikariDataSource dataSource;
static {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/my_database");
config.setUsername("root");
config.setPassword("password");
config.setMaximumPoolSize(10);
dataSource = new HikariDataSource(config);
}
public static void main(String[] args) {
try (Connection conn = dataSource.getConnection()) {
String insertSQL = "INSERT INTO my_table (column1, column2) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {
for (int i = 0; i < 1000; i++) {
pstmt.setString(1, "value" + i);
pstmt.setString(2, "value" + i);
pstmt.addBatch();
}
pstmt.executeBatch();
System.out.println("Batch insert completed successfully.");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
六. 数据库锁优化
1. 使用行级锁
避免使用表级锁,使用行级锁来减少锁争用。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class RowLockExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/my_database";
String user = "root";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
String lockSQL = "SELECT * FROM my_table WHERE id = ? FOR UPDATE";
try (PreparedStatement pstmt = conn.prepareStatement(lockSQL)) {
pstmt.setInt(1, 1);
pstmt.executeQuery();
// Perform update on the locked row
String updateSQL = "UPDATE my_table SET column1 = ? WHERE id = ?";
try (PreparedStatement updateStmt = conn.prepareStatement(updateSQL)) {
updateStmt.setString(1, "new_value");
updateStmt.setInt(2, 1);
updateStmt.executeUpdate();
}
conn.commit();
System.out.println("Row updated successfully with row lock.");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2. 乐观锁
使用版本号或时间戳进行控制,避免锁争用。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class OptimisticLockExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/my_database";
String user = "root";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
conn.setAutoCommit(false);
String selectSQL = "SELECT version, column1 FROM my_table WHERE id = ?";
try (PreparedStatement selectStmt = conn.prepareStatement(selectSQL)) {
selectStmt.setInt(1, 1);
try (ResultSet rs = selectStmt.executeQuery()) {
if (rs.next()) {
int version = rs.getInt("version");
String column1 = rs.getString("column1");
// Update the record with optimistic lock
String updateSQL = "UPDATE my_table SET column1 = ?, version = version + 1 WHERE id = ? AND version = ?";
try (PreparedStatement updateStmt = conn.prepareStatement(updateSQL