目录
实际应用中不可避免的存在并发场景,Redis也不例外,也会存在并发操作,比如用户下单时,有两个用户先从Redis查询到库存,然后同时下单,并发写操作,如果我们没做好控制,就可能导致数据被修改错,影响业务。为了保证并发访问的正确性,Redis提供了两种方法,分别是加锁和原子操作。
加锁是一种常用的方法,在读取数据前,客户端需要先获取锁,获取不到的话就无法操作。等一个客户端获得锁后,就会一直持有这把锁,直到客户端完成更新,才会释放锁。
加锁会有两个问题:
- 如果加锁操作多,会降低系统并发访问性能;
- Redis客户端加锁时,需要用到分布式锁,而分布式锁实现复杂,需要额外的存储系统来提供加锁操作。
原子操作是另一种提供并发访问控制的方法。原子操作是指执行过程中保持原子性的操作,而且原子操作执行时不需要再加锁,实现无锁操作。
并发控制
并发控制指多个客户端访问操作同一份数据的过程进行控制,以保证任何一个客户端发送的操作在Redis上执行具有互斥性。
并发访问控制对应的操作主要是修改操作。当客户端需要修改数据时,基本流程分成两步:
- 客户端先把数据读取到本地,在本地进行修改;
- 客户端修改完成后,再写回Redis
这个过程可以定义为:读取-修改-写回。当有多个客户端对同一份数据执行相同的操作时,就需要让该流程原子性的执行,这一执行过程的代码叫临界区代码。如下为伪代码
get data
update data
set data
首先是读取数据,然后进行修改,最后在写回修改后的数据。如果我们对临界区的代码没有进行控制,就会出现错误的数据。
如图所示,如果按照正确的逻辑处理,实际上的库存应该是90,如果没有并发控制,此时库存为95,库存会出现了错误。
出现这个现象的原因是,临界区代码中的客户端读取数据、更新数据、再写回数据涉及了三个操作,而这三个操作在执行时并不具有互斥性,多个客户端基于相同的初始值进行修改,而不是基于前一个客户端修改后的值再修改。
为了保证并发操作的正确性,可以通过加锁或原子操作来保证。
分布式锁
首先看下伪代码的实现,在临界代码区执行前后分别加上获取货和释放锁。下面是分布式锁的实现方式。
lock
get data
update data
set data
unlock