出门莫恨无人随,书中车马多如簇。这篇文章主要讲述设计AppServer面试讨论相关的知识,希望能为你提供帮助。
我在最近的系统设计访谈中遇到了以下问题:
设计与Cache和DB连接的AppServer。
【设计AppServer面试讨论】我想出了这个:
public class AppServer{
public Database DB;
public Cache cache;
public Value get(Key k){
Value res = cache.get(k);
if(res == null){
res = DB.get(k);
cache.set(k, res);
}
}public void set(Key k, Value v){
cache.set(k, v);
DB.set(k, v);
}
}
这段代码很好并且工作正常,但问题的后续跟进是:
- 如果有多个线程怎么办?
- 如果有多个AppServer实例怎么办?
- 突然,AppServer的性能下降了很多,我们发现这是因为我们的缓存一直缺失。缓存大小是固定的(已经是最大的)。我们怎么能防止这种情况?
- 我回答说我们可以使用Locks或Conditional Variables。在java中,我们可以为每个方法添加Synchronized以允许互斥,但是访问者提到这不是太有效并且只希望关键部分同步。
void set(Key k, Value v)
和Value get(Key k)
的1套方法中同步2条线,但是采访者也推动同步res = DB.get(k);
。我最后同意他,但不完全明白。线程是否具有独立堆栈和共享堆?因此,当一个线程执行get时,它将res存储在堆栈帧的局部变量中,即使另一个线程执行顺序执行,前一个线程仍保留其get值。然后每个线程设置它们各自的获取值。- 我们如何处理AppServer的多个实例?
版本系统和事件系统还有可能的解决方案吗?
- 可能的解决方案: L1,L2,L3缓存 - 层和更多缓存 区域/分段缓存 - 为用户组使用不同的缓存。 还有其他想法吗?
答案1
虽然JDBC“应该”是线程安全的,但有些驱动程序不是,我会假设Cache也不是线程安全的(虽然大多数缓存应该是线程安全的)所以在这种情况下,你需要制作您的代码的以下更改:
- 让两个领域最终
- 同步整个
get(...)
method - 同步整个
set(...)
method
get(...)
方法的行为取决于两件事:首先,可以看到来自set(...)
方法的更新,其次,缓存未命中然后仅由单个线程存储。您需要进行同步,因为在存在高速缓存未命中的情况下,只有一个线程执行昂贵的数据库查询。如果不同步整个get(...)
方法,或者拆分synchronized语句,则另一个线程也可能在查找和插入之间看到缓存未命中。我回答这个问题的方法老实说只是为了折腾整个事情。我会看看JCIP如何编写缓存并根据我的答案。
2
我认为你的队列解决方案很好。
我相信你的面试官意味着如果
AppServer
的另一个实例没有通过set(...)
的另一个实例缓存已经是AppServer
,那么它将在DB中查找并找到正确的值。如果你使用多个线程,这个解决方案是不正确的,因为2个线程可能是set(...)
ing冲突的值,然后缓存将有2个不同的值,同时取决于你的数据库的线程安全性,它甚至可能没有值所有。理想情况下,您永远不会创建多个
AppServer
实例。3
我没有足够的经验来专门评估这个问题,但是LRU缓存可能会稍微提高性能,或者使用哈希环缓冲区。这可能是一个延伸,但如果你想扔掉那里,或许甚至使用ML来确定最佳值,例如,预加载以保留在一天的某些时间,也可以工作。
如果您始终缺少缓存中的值,则无法改进代码。性能取决于您的数据库。
推荐阅读
- “APK被定义了多次”,app无法安装Android Studio
- Android LiveData和Room(getValue返回NULL)
- Android运行时权限对话框未显示
- 为什么选择Android应用程序项目的Kotlin()
- 如何在Visual Studio Community 2017中更改为深色主题
- 如何确定数字是否为C中的阿姆斯特朗数
- 如何在Swift中打印Pascal三角形
- 如何在C ++中打印Pascal三角形
- 如何使用Microsoft SQL Server Management Studio 17将MS SQL Server数据库导出到SQL脚本(数据库到sql文件)