设计AppServer面试讨论

时间:2017-12-22 17:28:53

标签: java multithreading caching architecture distributed-system

我在最近的系统设计访谈中遇到了以下问题:

设计与Cache和DB接口的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);
    }
}

此代码很好并且工作正常,但问题的后续跟踪是:

  1. 如果有多个线程怎么办?
  2. 如果有多个AppServer实例怎么办?
  3. 突然,AppServer性能下降了很多,我们发现这是因为我们的缓存一直在丢失。缓存大小是固定的(已经是最大的)。我们怎样才能阻止这种情况?
  4. 响应:

    1. 我回答说我们可以使用Locks或Conditional Variables。在Java中,我们可以为每个方法添加Synchronized以允许互斥,但是访问者提到这不是太有效,只希望关键部分同步。
    2. 我认为我们只需要同步void set(Key k, Value v)中的2个设置行和Value get(Key k)中的1个设置方法,但访问者也推动同步res = DB.get(k);。我最后同意他的意见,但并不完全明白。没有线程有独立的堆栈和共享堆?因此,当一个线程执行get时,它将res存储在堆栈帧的局部变量中,即使另一个线程执行顺序执行,前一个线程仍保留其get值。然后每个线程设置它们各自的提取值。

      1. 我们如何处理AppServer的多个实例?
      2. 我想出了像Kafka这样的分布式队列解决方案,每次我们执行set / get命令时我们都会对该命令进行排队,但他也提到set是正常的,因为该操作在cache / db中设置了一个值,但是如何你会为get返回正确的值吗?有人可以解释一下吗?

        还有可能的版本控制系统和事件系统解决方案吗?

        1. 可能的解决方案:
          • L1,L2,L3缓存 - 图层和更多缓存
          • 区域/分段缓存 - 为用户组使用不同的缓存。
          • 还有其他想法吗?
        2. 将提升所有富有洞察力的回应:)

1 个答案:

答案 0 :(得分:0)

1

虽然JDBC是"假设"为了线程安全,一些驱动程序不是,我会假设Cache也不是线程安全的(尽管大多数缓存应该是线程安全的)所以在这种情况下,你需要制作代码的以下更改:

  1. 将两个字段设为最终
  2. 同步整个get(...)方法
  3. 同步整个set(...)方法
  4. 假设没有其他方法可以访问上述字段,get(...)方法的行为取决于两件事:首先,可以看到来自set(...)方法的更新,其次,然后,缓存未命中仅由单个线程存储。您需要进行同步,因为在存在高速缓存未命中的情况下,只有一个线程执行昂贵的数据库查询。如果不同步整个get(...)方法,或者拆分synchronized语句,则另一个线程也可能在查找和插入之间看到缓存未命中。

    我回答这个问题的方法老实说只是折腾整个事情。我会看看JCIP如何编写缓存并根据它做出答案。

    2

    我认为您的队列解决方案很好。

    我相信您的面试官意味着如果AppServer的另一个实例没有缓存另一个set(...)实例已经AppServer的内容,那么它会查找并找到正确的值DB。如果您使用多个线程,则此解决方案将是不正确的,因为2个线程可能set(...)存在冲突值,然后缓存将具有2个不同的值,同时取决于数据库的线程安全性,它可能不会甚至都有价值。

    理想情况下,您永远不会创建AppServer的单个实例。

    3

    我没有足够的经验专门评估这个问题,但是LRU缓存可能会稍微提高性能,或者使用哈希环缓冲区。这可能是一个延伸,但如果你想扔掉那里,或许甚至使用ML来确定最佳值,例如,预加载以保留在一天中的某些时间,也可以起作用。

    如果您始终缺少缓存中的值,则无法改进代码。性能取决于您的数据库。