Redis如何解决大量并发问题(入门)

166 阅读5分钟

Redis是什么?

Redis(Remote Dictionary Server),是一种开源内存中的数据结构存储,可以理解其为一种非关系型的缓存数据库,它以键值(key-value)的方式在内存中存储数据。

Redis的使用解决了什么问题?

一句话,对于数据库大量并发的访问引发的问题。在类似于选课、抢火车票等大量并发的情境下,如果所有访问的请求都直接访问MySQL数据库,数据库会出现访问异常,出现脏读。换言之,MySQL数据库无法满足短时间内程序对已存储数据的大量快速的访问

举个选课的简单例子

选课的时候,学生一般选课的过程是:
(1)查询课程已选人数是否已达到最多人数限制-->
(2)修改课程已选人数-->
(3)把修改的数据更新到数据库

如果课程可选的剩余人数只有一人,而且有两个学生同时执行上面的事务,在并行系统中,很有可能出现以下情况:
(1)第一个学生查询到课程已选人数未满-->
(2)第二个学生查询到课程已选人数未满-->
(3)第一个学生修改课程已选人数-->
(4)第二个学生修改课程已选人数-->
(5)第一个学生把已修改的数据更新到数据库-->
(6)第二个学生把已修改的数据更新到数据库

可以看到,在这个人数已选余量只有一的情况下,两个学生都可以选到自己心仪的课程。显然,这扰乱了课程选择的正常秩序,这应该是教务处不想看到的局面。

Redis是如何处理类似于上面的大量并发的问题?

由于其直接访问内存中的数据,因此把Redis充当程序访问数据库的中间件,从而提高效率和缓解数据库服务器的压力。

简单来讲,Redis首先把MySQL数据库中会被大量访问的数据存储在内存中。当客户端发送访问数据的请求时,程序就直接访问Redis存储在内存中的数据。然后Redis再把相关数据更新到MySQL数据库中。

Redis采用了一些关键的技术来确保技术的高性能和可靠性,笔者认为主要有以下几点:

  • 采用单线程模型

何为单线程模型?

它只使用一个CPU来处理所有请求。

为什么可以如此设计?

因为Redis主要用于缓存和内存数据存储,内存的读写速度非常快。在这种情况下,CPU的计算速度往往可以满足处理请求的需求,不需要多线程的并行计算。

单线程模型有何用?

由于Redis不需要考虑多线程之间的同步、锁、竞争等问题,也不需要花费时间和资源在多线程之间的上下文切换上,使得Redis的设计和实现更简单,性能和效率更高。

  • 支持多种原子操作

何为原子操作?

将多个命令打包在一起,并确保它们在执行时要么全部成功,要么全部失败
Redis把多个命令打包在一起,将其视为整个事务。如果在执行事务期间出现错误(例如语法错误或事务中的某个命令失败),则整个事务将回滚,不会影响数据。如果所有命令都成功执行,则事务将提交,将所有更改应用到数据。因此,Redis可以通过原子化操作,来确保数据的完整性

举个在Redis中执行一个银行转账的例子:

MULTI
SET account1_balance 1000
SET account2_balance 500
DECRBY account1_balance 200
INCRBY account2_balance 200
EXEC

在这个示例中,我们使用MULTI开始事务,并依次执行了四个命令。如果在执行事务期间任何一步出现问题,例如第三个命令由于余额不足而失败,整个事务将回滚,不会应用任何更改。只有当所有命令都成功执行时,事务才会提交。

  • 支持乐观锁加锁

何为乐观锁?

简单来讲,乐观锁是一种用于处理并发访问共享数据的机制,它的核心思想是:在读取数据时不加锁,但在更新数据之前,会检查是否有其他线程或进程已经修改了数据。如果检测到其他修改,就会阻止当前操作,以避免数据不一致性。

如何检查是否有其他线程或进程已经修改了数据?

当一个线程想要读取共享数据时,它会获取数据的当前版本号,并将其存储在本地。然后当其想要修改共享数据,并尝试把修改后的数据写回时,线程会检查当前数据的版本号是否仍然与它在读取时存储的版本号相同。如果相同,说明在此线程读取数据后没有其他线程修改过它,那么写操作将成功,版本号会更新。但如果版本号不同,说明在读取数据后有其他线程修改了它,那么写操作将失败。

为何要用乐观锁加锁?

通过版本控制来处理并发访问的机制,它允许多个线程或进程在不加锁的情况下读取数据,但在写操作时会检查数据是否被其他线程修改,从而保证数据的一致性和可靠性。