开发者学堂课程【高校精品课-厦门大学 -JavaEE 平台技术:瓶颈分析】学习笔记,与课程紧密联系,让用户快速学习知识
课程地址:https://developer.aliyun.com/learning/course/80/detail/15933
瓶颈分析
内容介绍:
一. 瓶颈的定义
二. 权限的教列
三. 教权限
四. 日志
瓶颈的这个部分其实是一个很难的问题,因为要讨论瓶颈,是要对它的需求和技术都要很了解,才会知道它的瓶颈是什么。
瓶颈产生的原因是因为需求,解决瓶颈的方案要靠技术的方案来解决。那么把这个设计的目标列出来,在这个系统中间它的瓶颈是什么?为什么把瓶颈放在一个比较早的阶段来讨论?因为会发现整个设计的展开都是围绕着这些瓶颈来设计的,就像签到系统,为什么它签不上去?它根本就没考虑瓶颈问题,就想做个签到的功能而已,辛辛苦苦做完以后发现一签到它就死机。特别是某一个时间点,如果上课的人
特别多,它就会死机。
如果先分析了,它就会出现慢和卡住的这种情况。在整个设计时就会走一个不同的路线,这个道路就不同了。设计的工作量做出的东西的工作量是完全不一样的。所
以可以看到在设计时在做的两个事情,第一个是核心是数据。
数据是跟着需求来的,可以看到er图的不断的完善,Api 的不断完善,其实对于每一个模块的需求在做不断的细化,这个是细节的部分。还有一个部分就是瓶颈,因为瓶颈决定了整个系统的体系结构,已经决定了后面整个的设计方向。按照一个什
么样的方式去设计这个系统。所以瓶颈分析是在进入详细设计之前一定要做的。
一.瓶颈的定义
要尽可能的从需求的角度去考虑这样的一个系统,它的瓶颈是什么?从以下这张图能看出这个系统的瓶颈在哪吗?唯一登陆为什么是瓶颈?对于瓶颈的定义是什么?
首先描述什么叫做瓶颈,瓶颈就是这个机器会卡住,不卡住就不叫做瓶颈。首先会
卡住它的地方就是瓶颈,为什么唯一登录会卡住它?唯一登录就最多卡住这个用户不会卡住其他的用户。如果用户一登录也会卡住的,那也很正常。
瓶颈会不会对所有的人都卡住?这个是优先要解决的。对于所有的用户都会卡住,比如签到系统,一旦卡住所有人都卡住都签不了,整个系统功能做的再完善也没有任何意义。签到签不进去以后再看报表就没有任何意义了。所以这个系统的瓶颈在哪?
二.权限的教列
首先说一个会卡住的瓶颈是日志,但是日志放到后面,这是小问题。首先说个大问题就是权限的教列,这个100%会卡住,因为所有的操作一开始都要教权限,一个api是否能进来,都要去加强权限。所以这是一个非常高频的,而且是所有人都要过的一道关口。那如果它慢,大家都会卡住,所以教权限是一个瓶颈,还有刚才的日志。为什么日志跟教权限很像,因为做完以后都要做日志。日志的每一个动作都要写日志,如果写日志的这个东西很慢,那么所有都会慢,它不是只影响某一个api。
就跟权限的问题一样的,如果它慢所有的 api 都会慢。日志也是一样的,如果写字慢所有东西都会慢。
这两个问题正好是两个有代表性的问题。教权限是读的问题,教权限其实没有写,它是要去获得当前用户的权限是什么,它全从数据库里把东西读出来。而写日志是写的问题,读的问题的速度比写的问题的速度要快很多,当然要看具体读多少写多少。因为这个问题中,可以看到教权限是要读一堆的东西,读一堆东西,当然会很慢。如果读一个很少的东西,那它会相对比较快,但是写就会比读更慢。
三.教权限
首先介绍教权限的这个部分,它的权限是一个要读出一堆东西的操作。那它要读什么?
可以看到后面的对象模型,在 er 图的基础上给它做了一个对象模型,这个对象模型的主要的目的就是为了教权限。就是一个用户,他要知道他的权限是什么,需要知道这个用户的角色所拥有的权限。用户所代理的用户,代理用户的角色,还有这些角色的权限,把这些东西全部捞出来,它才能算出来这个用户的权限是什么。
虽然它是个读的操作,但是它读的东西太多还一直读不完,读一个代理用户要读很多次才能读完,所以就是因为读的东西太多而造成的这样一个瓶颈,而且是高频
的,读的又多又高频,所有人都要用。啊。那怎么解决呢?
按常用的办法,读的这个方式来说,第一次要做缓存,做缓存的主要的前提就是这个东西是读多写少。就是读得多,修改的机会是非常少才能做缓存。如果这个东西
读写都很频繁,那么做缓存是没有用的。因为缓存起来就意味着它跟数据库里的东西是有两份,如果数据库改了,缓存肯定要改,要不就不准确了。
如果数据库和缓存不停的改,它就会变得很慢,所以做缓存有一个前提,做缓存必须是读多写少,有这样的一个前提才能做缓存。对于权限来说,它就是一个读多写少的。因为大量的访问都是要去教权限,而真正会去修改一个用户的权限的概率是要非常多的。这样一个读多写少的东西,它是适合做缓存的。缓存一定会用到Redis,但因为现在 Java1还没介绍到那个部分,所以先不讲 redis 里面的特性。
用 Redis 来存这个缓存,Redis 是内存中间的。所以知道内存中间的特征就是快,缓存就是要快。不要去读数据库,内存的访问速度比数据库要快多了。
第二就是贵,所以内存的一个字就是快而且贵。那就意味着不能把前面的这个对象放到内存里,前面这个东西都放在内存里会很浪费。发内存的用户对象是为了教权
限,所以只把权限放进去,其他的什么东西都不需要。
权限是有这么复杂的一个过程,所以不能把这么复杂的结构放进去,应该把这个东西读出来后,算出这个用户最终的权限是多少,再将形成的权限的位置放到完成里面,其他什么都没有,就是一个用户的 id,还有权限的位。权限位会用到 Redis中Bitmap 的结构,因为没有介绍到 Redis,所以不去做更深入的讨论,就只用知道最后用户的权限全是用二进制位放置的。
假如整个系统有500个功能,那就是一个500长度的二进制位。它哪些权限是有的,那个位就是一,哪些权限没有就是零,所以教权限就是一个很简单的事情,在 Redis中间看对应位是不是一就好。因为如果是一,那就有权限,就可以通过。这是一个在 Redis 中间存的比较少,但是速度比较快的一种方式。因为这个部分涉及到Redis的一些技术,可以看到要去解决瓶颈,首先要了解需求,从需求中间分析出来
可能的瓶颈是什么,第二解决瓶颈的方式肯定是用技术的手段去解决。
所以这样就造成了第一个详细设计的问题,在下周一再详细介绍,今天只是在做瓶颈分析。
产生的第一个详细设计的问题就是,第一要从数据库将这个东西捞出来。所以要设计 map的方法,将这些东西捞出来。第二要把捞出来的这些东西变成一个 Bitway,再将这个 bitway存到 Redis里。如果对 Redis了解,就会知道存进去不是把那些东西永久的放到 Redis里面,因为Redis很贵,如果有一万用户,每个用户都放进去,那么 Redis就没空间了。
而且这个东西会发生改变,这里有一个代理机制,它代理期过了以后权益会发生改变,所以放进去时,要给 Redis一个有效期。就是它到了有效期后就会将它清除。再去数据库里读,再放到 Redis里,这是产生的第一个详细设计的需求,设计的需
求就是从这里来的。
建议在做这个设计时,不要从真删改查做起,很多人做的设计开始就想去做真删改查,权限在这个系统中间会发现所有的真删改查的目的都是为了教权限,所以上手就从最难的最关键的部分上手,把这个问题解决,所有的真删改都是为它服务的,
如果这个问题不能解决,就算将上一个对子做完,最后发现权限教不成功,那么真
删改查就相当于白做。
所以在做软件开发的过程中间,先做什么后做什么,其实是非常重要的。
在软件的开发过程中间,所有人都会考虑的一个问题就是,如何以最小的成本获取最大的收益。花的工作量最少,但是最后得到的效果是最大的。这个过程中间有很多东西,首先第一点就是要避免浪费,如果因为需求的变化外客观的原因造成的变更,作为程序员来说是控制不住的。但作为主观的原因所产生的这个浪费是要去控制的,目前来说,在学习阶段可能更加可控的是主观阶段而不是客观阶段,因为客观阶段是由环境决定的,但是主观阶段就是到底做什么,先做什么,再做什么,保
证做的东西不浪费,这个就是主观阶段。
比如这个例子中间,在整个里面要最先下手的是它最核心的功能,因为整个模块就是为了这个功能而存在,如果不能教权限全系统就没有任何意义了。所以先把这个部分完成了以后,就会发现一些问题。
这些问题可能会引起er图的改变,可能会引起很多地方的改变。但把这些东西都做完以后,它的改变确定了,再去做它们的真删改查。要不然如果真删改查做完再来
做这块,发现它要改,那就要去重新真删改查通过,所以先后的顺序是非常重要的。
但是会发现这是有难度的。它不太适合于一个新手,一开始就直接奔最难的地方,
如果技术还没达到这个层次,可能想先挑简单的去做,把握不住难的地方就挑简单的去做,但这样会浪费时间。
所以首先要把自己的技术修炼上去,这样在做东西的时候就可以直接奔最难的地方
下手,先把难的东西解决了,简单的东西的变化就变得可控了。这个是第一个瓶颈,就是在教权限的部分。
四.日志
第二个瓶颈是什么?就是前面提到的日志。日志为什么是瓶颈,因为它是写。可以去看一下所有的设备,无论是磁盘,机械磁盘还是ssd,基本上读写的速度是二比一。
就是读比写要快一倍。那写又是每一个都要写的动作。因为它没有互斥问题, 写日志就直接写进去就好了,所以影响它的因素就是写的速度,就是在同一个物理设备上的写的速度,当然可以说怎么样用多个物理设备使得它写的时候将它分开,这是后话,但是对于单个物理收缩倍来说这个日志就是它写的速度的瓶颈。
其实在订单系统中间有一个更难处理的瓶颈,那个瓶颈是什么?是订单的时候扣库
存,也是个写的操作。而且是一个互斥的写的操作。就是秒杀时所有人都要去扣库存,这是是个互斥的写的操作,而不是一个不互斥的。那问题就更难处理了。
因为既要保证很多人都能把买东西买下来,就有并发性。它又是互斥的,所以这是个矛盾。那怎样解决这个矛盾,这是做订单模块的同学很有挑战性的部分。可能整个系统中间只有这个库存是一个互斥的写操作,这个是很难处理的一个部分。
接着回到写日志的这个问题,写日志这个问题写的速度是它的瓶颈,怎么样提高呢?提高不了因为写不快,最多插入一个 ssd,就算是 ssd,如果瞬间有一万个用户充电,它也写不下去。所以要去看如果写不下去,可以采用另外一种方式来处理,称之为异步的方式。它既然写不下去就把它设置成发导弹一样,打出去就不用管,让另外一个东西去写,写完就完成了,不用再管它了。
那这个东西是什么?这个东西称之为消息服务器。就是不能简单的写数据库,要把它交给一个消息服务器去写,消息服务器就会慢慢的写。因为对于日志可以分析它的要求,它其实并不要求及时写进去,不像库存,扣了要及时扣掉,因为不能卖出负数。而这个日志可以慢慢写,反正横竖只要写进去就好了,至于是一分钟以后写进去,还是十分钟以后写进去,都可以接受。所以可以交给一个异步的消息服务器去完成。
当然这个消费服务器有个要求,就是它一定要有持久性。叫你写你一定要写,不叫你写你就不写。比如告诉你写,但是还没写进去,现在停电了怎么办,拿点电再写回去。这个其实很难做,不要以为这是个简单的东西。在这个学期会用到阿里的
Rocket MQ,而不是在网上看得到的 Rabbit MQ。
往年会改成 Rabbit MQ,现在改成 Rocker MQ,Rocket MQ让它写它一定会写的,它里面做了一个不知道什么样的机制,停了电它也能写回去。所以它有一个很强悍的机制,可以去研究一下它为什么停了电也能写回去。只要它接收到这个消息,就算停了电也能够将这个东西写回去,所以打算用这样的机制。
这样就使得写日志的过程对于请求来说,就告诉消息服务器去写,就可以认为这个事情已经完成了,不要等到写完了以后再告诉,这样可以降低每一个服务的处理时间,因为每个时间都提快了以后整个的处理量,吞吐量就会加大。
所以这是去分析了这个系统的两个瓶颈,认为第一个是日志问题。是读的问题,是读多写少的问题,所以用redis缓存来解决,第二个是写日志的问题,这是一个写的问题,而且它不要求及时写,所以用一个消息服务器去解决,这就是瓶颈分析。