如何将对象存储在内存中并在 Max(amount, time) 之后将它们刷新到目的地
How to store objects in memory and flush them to a destination after Max(amount, time)
我的 Spring Boot
应用程序获取输入 Student
并需要将其写入数据库。
我可以在获取 Student
对象后立即写入数据库,但我希望通过在内存中累积多个学生然后将大量 [=13] 保存到数据库来提高效率=].
所以我正在寻找一种解决方案,可以缓存 Max(some duration, number of students)
的学生,并可以在达到限制后批量保存学生。
有一个选项可以用Thread
之类的方式编写代码,但似乎这个问题之前可能已经解决了。
一般的想法是有一个队列,其中包含等待刷新的对象。
刷新将由 2 个事件触发:按计划(超出某些持续时间)和按大小(超出队列大小)。
在纯 Java 中可以使用 Executors.newSingleThreadScheduledExecutor()
:
实现
public abstract class FlushingCache<T> {
private final Duration maxDuration;
private final int maxSize;
private final List<T> queue = new ArrayList<>();
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
public FlushingCache(Duration maxDuration, int maxSize) {
this.maxDuration = maxDuration;
this.maxSize = maxSize;
executor.scheduleWithFixedDelay(this::doFlush,
maxDuration.getSeconds(),
maxDuration.getSeconds(),
TimeUnit.SECONDS);
}
public synchronized void enqueue(T element) {
println("Enqueueing element " + element);
queue.add(element);
if (queue.size() >= maxSize) {
doFlush();
}
}
private synchronized void doFlush() {
List<T> batch = new ArrayList<>(queue);
println("Flushing batch " + batch);
flush(batch);
queue.clear();
}
// The login of flushing to DB will be implemented in the sub-classes
protected abstract void flush(List<T> batch);
}
用下面的测试数据
public static void main(String[] args) {
FlushingCache<String> studentsCache = new FlushingCache<>(Duration.ofSeconds(10), 3) {
@Override
protected void flush(List<String> batch) {
println("### FLUSH TO DB " + batch);
}
};
studentsCache.enqueue("1");
studentsCache.enqueue("2");
studentsCache.enqueue("3");
studentsCache.enqueue("4");
}
private static void println(String message) {
System.out.println(LocalTime.now().withNano(0).toString() + ": " + message);
}
结果是
16:07:17: Enqueueing element 1
16:07:17: Enqueueing element 2
16:07:17: Enqueueing element 3
16:07:17: Flushing batch [1, 2, 3]
16:07:17: ### FLUSH TO DB [1, 2, 3]
16:07:17: Enqueueing element 4
16:07:27: Flushing batch [4]
16:07:27: ### FLUSH TO DB [4]
队列大小达到 3 或每 10 秒刷新一次到数据库。
在 Spring 启动应用程序而不是 ScheduledExecutorService
你可以使用 @Scheduled
.
启用调度 @EnableScheduling
@Component
public class StudentFlushingCache {
private final List<T> queue = new ArrayList<>();
@Value("${student.flushing-cache.max-size}")
private final int maxSize;
@Scheduled(
fixedDelayString = "${student.flushing-cache.fixed-delay}",
initialDelayString = "${student.flushing-cache.initial-delay}")
public void flushPeriodically() {
doFlush();
}
public synchronized void enqueue(T element) {
/* ... */
}
private synchronized void doFlush() {
/* ... */
}
}
您可以尝试这样的操作:
1.创建单例class来容纳Students
public enum MyCache {
INSTANCE(new HashMap<>());
private Map<String, Student> students;
private MyCache(Map<String, Student> students) {
this.students = students;
}...getter, setter}
创建 StudentService,并添加将新 Student 推入缓存的方法:MyCache.INSTANCE.getStudents().push(...)
创建计划服务以保留缓存:
@配置
@EnableScheduling
public class 预定服务 {
@Scheduled(fixedDelay = 60*60*1000)
public void scheduledTask() {
1.获取缓存
2.检查是否达到限制(num students)
3. 坚持学生 - studentRepository.saveAll(学生)
4. 清空缓存 - list.clear()
}
}
我的 Spring Boot
应用程序获取输入 Student
并需要将其写入数据库。
我可以在获取 Student
对象后立即写入数据库,但我希望通过在内存中累积多个学生然后将大量 [=13] 保存到数据库来提高效率=].
所以我正在寻找一种解决方案,可以缓存 Max(some duration, number of students)
的学生,并可以在达到限制后批量保存学生。
有一个选项可以用Thread
之类的方式编写代码,但似乎这个问题之前可能已经解决了。
一般的想法是有一个队列,其中包含等待刷新的对象。 刷新将由 2 个事件触发:按计划(超出某些持续时间)和按大小(超出队列大小)。
在纯 Java 中可以使用 Executors.newSingleThreadScheduledExecutor()
:
public abstract class FlushingCache<T> {
private final Duration maxDuration;
private final int maxSize;
private final List<T> queue = new ArrayList<>();
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
public FlushingCache(Duration maxDuration, int maxSize) {
this.maxDuration = maxDuration;
this.maxSize = maxSize;
executor.scheduleWithFixedDelay(this::doFlush,
maxDuration.getSeconds(),
maxDuration.getSeconds(),
TimeUnit.SECONDS);
}
public synchronized void enqueue(T element) {
println("Enqueueing element " + element);
queue.add(element);
if (queue.size() >= maxSize) {
doFlush();
}
}
private synchronized void doFlush() {
List<T> batch = new ArrayList<>(queue);
println("Flushing batch " + batch);
flush(batch);
queue.clear();
}
// The login of flushing to DB will be implemented in the sub-classes
protected abstract void flush(List<T> batch);
}
用下面的测试数据
public static void main(String[] args) {
FlushingCache<String> studentsCache = new FlushingCache<>(Duration.ofSeconds(10), 3) {
@Override
protected void flush(List<String> batch) {
println("### FLUSH TO DB " + batch);
}
};
studentsCache.enqueue("1");
studentsCache.enqueue("2");
studentsCache.enqueue("3");
studentsCache.enqueue("4");
}
private static void println(String message) {
System.out.println(LocalTime.now().withNano(0).toString() + ": " + message);
}
结果是
16:07:17: Enqueueing element 1
16:07:17: Enqueueing element 2
16:07:17: Enqueueing element 3
16:07:17: Flushing batch [1, 2, 3]
16:07:17: ### FLUSH TO DB [1, 2, 3]
16:07:17: Enqueueing element 4
16:07:27: Flushing batch [4]
16:07:27: ### FLUSH TO DB [4]
队列大小达到 3 或每 10 秒刷新一次到数据库。
在 Spring 启动应用程序而不是 ScheduledExecutorService
你可以使用 @Scheduled
.
启用调度 @EnableScheduling
@Component
public class StudentFlushingCache {
private final List<T> queue = new ArrayList<>();
@Value("${student.flushing-cache.max-size}")
private final int maxSize;
@Scheduled(
fixedDelayString = "${student.flushing-cache.fixed-delay}",
initialDelayString = "${student.flushing-cache.initial-delay}")
public void flushPeriodically() {
doFlush();
}
public synchronized void enqueue(T element) {
/* ... */
}
private synchronized void doFlush() {
/* ... */
}
}
您可以尝试这样的操作: 1.创建单例class来容纳Students
public enum MyCache {
INSTANCE(new HashMap<>());
private Map<String, Student> students;
private MyCache(Map<String, Student> students) {
this.students = students;
}...getter, setter}
创建 StudentService,并添加将新 Student 推入缓存的方法:
MyCache.INSTANCE.getStudents().push(...)
创建计划服务以保留缓存:
@配置 @EnableScheduling public class 预定服务 {
@Scheduled(fixedDelay = 60*60*1000) public void scheduledTask() { 1.获取缓存 2.检查是否达到限制(num students) 3. 坚持学生 - studentRepository.saveAll(学生) 4. 清空缓存 - list.clear() } }