第一、使用Spring Boot将数据库查询的数据存入Redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Spring Boot会自动配置Redis连接,并提供RedisTemplate和StringRedisTemplate用于操作Redis。
数据库查询并存入Redis
首先,我们需要编写一个服务类,用于从数据库中查询数据并存入Redis中。假设我们有一个User实体类和一个UserRepository接口用于操作数据库:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String email;
// getters and setters
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 自定义查询方法
User findByUsername(String username);
}
接下来,编写一个服务类UserService,在Spring Boot启动时从数据库中查询用户数据并存入Redis中:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@PostConstruct
public void init() {
List<User> userList = userRepository.findAll();
for (User user : userList) {
stringRedisTemplate.opsForValue().set("user:" + user.getId(), user.getUsername());
}
}
}
在上面的代码中,我们通过@PostConstruct注解标记的init方法在Spring容器初始化时执行,从数据库中查询所有用户数据并存入Redis中。我们使用StringRedisTemplate向Redis中存入数据,键值格式为user:{id},值为用户名。
第二、下面是一个基本的实现方案:示例提供了一个基本框架
- 配置Spring Boot应用:
- 配置数据源(DataSource)以连接到数据库。
- 配置Redis客户端。
- 编写JDBC查询逻辑:
- 使用
JdbcTemplate
或NamedParameterJdbcTemplate
来执行SQL查询。 - 采用分页或游标的方式获取数据,以便每次只加载一部分数据到内存中。
- 将数据写入Redis:
- 使用RedisTemplate或操作Redis的API来存储数据。
- 实现逻辑:
- 在一个循环中逐批次地读取数据,并将其写入Redis。
以下是一个示例代码片段,用于从JDBC数据源读取数据并将其写入Redis:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class DataMigrationService {
private static final int BATCH_SIZE = 1000; // 每次查询的数据量
private static final String QUERY = "SELECT * FROM my_table LIMIT ?, ?"; // 分页查询语句
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 将数据从数据库迁移到Redis
*/
@Async
public void migrateDataToRedis() {
int offset = 0;
do {
List<Map<String, Object>> rows = jdbcTemplate.queryForList(QUERY, offset, BATCH_SIZE);
for (Map<String, Object> row : rows) {
// 假设我们使用row中的"id"作为Redis的键
Long id = (Long) row.get("id");
// 存储到Redis
redisTemplate.opsForValue().set("data:" + id, row);
}
offset += BATCH_SIZE;
} while (rows.size() == BATCH_SIZE); // 如果查询结果等于BATCH_SIZE,则继续查询
}
}
注意事项:
- 异步处理:可以使用
@Async
注解来异步执行迁移任务,以减少对主应用程序的影响。 - 错误处理:需要考虑异常处理机制,确保数据完整性和一致性。
- 性能优化:根据实际情况调整BATCH_SIZE的大小,找到最佳的平衡点。
- 资源管理:确保在处理大量数据时合理管理数据库连接和Redis连接,避免资源耗尽。
以上示例提供了一个基本框架,实际应用中可能需要根据具体的业务需求进行调整。
第三,一个真实的案例,查询数据库放入到布隆过滤器中。
通过runner来支持启动时把查询的数据放到了布隆过滤器。
在这个项目里,还有每天的定时任务来执行该动作。
NbigscreenBloom.java
package com.cqsym.nbigscreen.bloomfilter;
import com.cqsym.nbigscreen.config.DataSourceRepository;
import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Component
public class NbigscreenBloom {
private static final Logger log = LoggerFactory.getLogger(NbigscreenBloom.class);
private static final int BATCH_SIZE = 10000; // 每次查询的数据量
private static final int BloomFilter_init_size = 1000000; // 初始化布隆过滤数据量,100万。
private static final int BloomFilter_size = BloomFilter_init_size * 2; // 布隆过滤数据量大小,200万。
public static BloomFilter tddBloomFilter;
public static BloomFilter toBloomFilter;
@Autowired
@Qualifier(value="secondDataSourceRepository")
private DataSourceRepository secondDataSourceRepository;
public void initForDay() {
//tddBloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), 10000000, 0.01);
tddBloomFilter = BloomFilter.create(Funnels.longFunnel(), BloomFilter_size, 0.01);
toBloomFilter = BloomFilter.create(Funnels.longFunnel(), BloomFilter_size, 0.01);
try {
log.info("initForDay ---------- 睡5秒 开始 ... ");
TimeUnit.SECONDS.sleep(5);
log.info("initForDay ---------- 睡5秒 完成。 ");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("initForDay ---------- start initForTdd() ... ");
new Thread(() -> {
log.info("initForDay1 ---------- start initForTdd() ... ");
initForTdd();
log.info("initForDay1 ---------- end initForTdd(). ");
},"initForTdd线程").start();
log.info("initForDay ---------- end initForTdd(). ");
log.info("initForDay ---------- start initForTo() ... ");
new Thread(() -> {
log.info("initForDay1 ---------- start initForTo() ... ");
initForTo();
log.info("initForDay1 ---------- end initForTo(). ");
},"initForTo线程").start();
log.info("initForDay ---------- end initForTo(). ");
}
public void initForTdd() {
final String sql0 = "select count(1) from t_dispatch_detail ";
final String sql = "select tdd_id from t_dispatch_detail limit ? , ? ";
//int offset = 0;
//int offset = 20000000;
int offset = secondDataSourceRepository.queryForListSingleColumn(sql0, Integer.class).get(0)-BloomFilter_init_size;
log.info("initForTdd ---------- First offset: " + offset);
List<Long> rows;
do {
log.info("initForTdd ---------- offset: " + offset);
rows = secondDataSourceRepository.queryForListSingleColumn(sql, Long.class, offset, BATCH_SIZE);
for (Long row : rows) {
tddBloomFilter.put(row);
}
offset += BATCH_SIZE;
} while (rows.size() == BATCH_SIZE);
log.info("initForTdd ---------- end");
}
public void initForTo() {
final String sql0 = "select count(1) from t_order ";
final String sql = "select to_id from t_order limit ? , ? ";
//int offset = 0;
//int offset = 12000000;
int offset = secondDataSourceRepository.queryForListSingleColumn(sql0, Integer.class).get(0)-BloomFilter_init_size;
log.info("initForTo ---------- First offset: " + offset);
List<Long> rows;
do {
log.info("initForTo ---------- offset: " + offset);
rows = secondDataSourceRepository.queryForListSingleColumn(sql, Long.class, offset, BATCH_SIZE);
for (Long row : rows) {
toBloomFilter.put(row);
}
offset += BATCH_SIZE;
} while (rows.size() == BATCH_SIZE);
log.info("initForTo ---------- end");
}
}