前言
上个月4号通过阿里工作的学长进行内推,7天简历评估,11号接到电话面试,尽管猝不及防回答仓促,但好在前期准备充分,通过。3天后进行现场面试,通知时间为早上10点。当日设了七点闹钟,结果五点五十三分惊醒后再无法入睡,起床,重新翻看之前做的笔记和重点,在lintcode上找了几道可能性较大的题进行练手。10点准时在蚂蚁金服总部开始面试,十点四十七分结束。15号收到通知,现场面通过,16号进行HR面,22号收到Offer。
面试内容如下:
1.为什么要使用分布式锁
使用分布式锁的目的,无外乎就是保证同一时间只有一个客户端可以对共享资源进行操作。
1.1举一个很长的例子
系统 A 是一个电商系统,目前是一台机器部署,系统中有一个用户下订单的接口,但是用户下订单之前一定要去检查一下库存,确保库存足够了才会给用户下单。由于系统有一定的并发,所以会预先将商品的库存保存在 Redis 中,用户下单的时候会更新 Redis 的库存。此时系统架构如下:
但是这样一来会产生一个问题:假如某个时刻,Redis 里面的某个商品库存为 1。
此时两个请求同时到来,其中一个请求执行到上图的第 3 步,更新数据库的库存为 0,但是第 4 步还没有执行。
而另外一个请求执行到了第 2 步,发现库存还是 1,就继续执行第 3 步。这样的结果,是导致卖出了 2 个商品,然而其实库存只有 1 个。
很明显不对啊!这就是典型的库存超卖问题。此时,我们很容易想到解决方案:用锁把 2、3、4 步锁住,让他们执行完之后,另一个线程才能进来执行第 2 步。
按照上面的图,在执行第 2 步时,使用 Java 提供的 Synchronized 或者 ReentrantLock 来锁住,然后在第 4 步执行完之后才释放锁。
这样一来,2、3、4 这 3 个步骤就被“锁”住了,多个线程之间只能串行化执行。
当整个系统的并发飙升,一台机器扛不住了。现在要增加一台机器,如下图:
增加机器之后,系统变成上图所示,假设此时两个用户的请求同时到来,但是落在了不同的机器上,那么这两个请求是可以同时执行了,还是会出现库存超卖的问题。
因为上图中的两个 A 系统,运行在两个不同的 JVM 里面,他们加的锁只对属于自己 JVM 里面的线程有效,对于其他 JVM 的线程是无效的。
因此,这里的问题是:Java 提供的原生锁机制在多机部署场景下失效了,这是因为两台机器加的锁不是同一个锁(两个锁在不同的 JVM 里面)。
那么,我们只要保证两台机器加的锁是同一个锁,问题不就解决了吗?此时,