首先,磁盘IO才是出现系统性能瓶颈的罪魁祸首
我们现在使用的关系型数据库中,为了保证数据的完整性,都会有锁的机制。也就是因为锁的存在,让数据的高并发的情况下,效率有所下降。当然,更重要的是,数据库中的数据是通过文件的形式存放在磁盘中,磁盘的读写速度是非常有限的,因此,高并发的情况下,数据库非常容易成为系统性能的瓶颈所在。
如何让系统的能够支持高并发的场景,不让数据库成为瓶颈呢?
既然系统的瓶颈来源于数据库的IO,那么减少数据库的IO自然就能够有效的提高系统的性能,能适应高并发的场景,最有效的方式就是缓存。
一般缓存我们有两种方式:本地缓存和分布式缓存。
本地缓存其实消耗的是应用服务器的资源,如果系统规模很小的话,这种方式比较适用。但是对于高并发的系统来说,本地缓存会导致应用程序可用的内存下降,这无疑会导致应用程序的服务器更容易出现瓶颈,并不符合我们提高系统性能的目的。所以,我们更愿意选择分布式缓存。
分布式缓存常用的中间件有Redis和Memecachee,我们将读取比较频繁,但是变化不是特别频繁的数据放在缓存之中,不让这部分读的请求穿透到数据库中,就能够有效地减少数据库读的IO次数。而缓存其实是会被常驻在内存之中,读取缓存并不会导致磁盘额IO,并发能力是非常高的。使用缓存,我们系统的并发能力也就能够得到非常可观的提升。
当然,缓存并不能解决所有的问题,因为数据库的IO瓶颈并不一定全部都是读,写也一样会产生IO。而缓存只能解决读IO所产生的问题,让用户去读缓存,但是写的情况一样会穿透到数据库中。
如果解决写的高并发呢?
写的高并发分成了很多种情况,一种是写的总请求数量其实并不高,但是主要集中在某一个时段,所以会导致这个时间的资源占用瞬间提高,但是服务器资源的整体利用率却并不高。例如:秒杀场景。
还有一种情况是些的总请求数量特别高,让数据库的服务器资源占用情况非常高,导致系统的效率下降。例如:双11当天下单。
如果是特定时段的写请求高并发,最好的解决方案是使用队列(例如:RabbitMQ)。我们把写的请求放到队列里面,返回给用户一个成功但需等待的消息,然后让队列慢慢的处理。这样,我们的数据库压力其实是被队列中间件分担了,数据库我们可以根据资源的情况,多线程的对队列里的消息进行消费,这样写的请求就被控制在了一个安全线内。如果我们的写请求一直都很高,那么队列其实并不能解决数据库的问题,反而会导致队列中效率的堆积,影响系统效率。那么我们就必须换一个方式,那么分库分表就是一个解决写瓶颈的有效方式了。
将数据平均的插入到两个不同的数据库中,让单个数据库的写IO降低一半,这样,我们的数据库效率就能够有效的被提高了。当然,数据库分库以后会存在很多的排序分页的问题,这个又是一个新的问题,我这里就不多说了。
最后在说一下一个架构理念吧
我们在做架构设计的时候,尽量要考虑请求的过滤,不要把所有的请求全部都最终落到数据库上,这样我们的数据库就非常容易发生问题,导致整个系统崩溃。我们可以在前端、接口都考虑一下请求的过滤问题,减轻数据库的负担。也就是说,我们的请求最终会形成一个漏斗。
例如:前端需要考虑用户如果重复多次的点击一个按钮,我们不要次次都回到接口中甚至回到数据库中,尽量使用前端的通过有效的方式能够过滤一部分情况。
而在接口中也是一样,能够查询缓存的数据,就不要去查数据库了,效率也高,系统也高。当然,对于高并发来说,还有太多太多可以谈的,这些都只是一些相对基础的知识。也欢迎大家更多的提出自己的经验,大家相互学习。