电商购物系统算是目前软件技术落地的很大的一个发展方向,主要以阿里为主导的电商购物系统占据整个行业的半壁江山,是直接和价值挂钩的重要业务方向。

架构演进

电商系统也是和其他系统一样一步步演进的,架构演进都是通用的,可以看这里

通用架构

目前电商的整体架构流程如下,并且在每一层如何实现在高并发,大流量的情况下,最大程度能够保证网站的健康。主要是流量控制,让用户的流量像漏斗模型一样逐层减少,让流量始终处于可控的范围之内。

  1. 客户端可以是各种终端,可以是web,进行各种操作。在web这边,主要是限流,可以使用js技术,比如购买按钮置灰,可以减少大量的重复请求。
  2. CDN层,CDN存储静态页面缓存技术。在这一层主要是缓存,很多的静态资源存储在cdn中,能够快速对请求进行返回。
    • 限制用户维度访问频率:针对同一个用户( Userid 维度),做页面级别缓存,单元时间内的请求,统一走缓存,返回同一个页面。
    • 限制商品维度访问频率:大量请求同时间段查询同一个商品时,可以做页面级别缓存,不管下回是谁来访问,只要是这个页面就直接返回。
  3. 接入层,nginx等负载均衡技术。这一层主要是限流截流,比如相同请求,只转发一个,分发到不同的服务实例,限制同一个ip的多次请求。
  4. 然后将请求放到MQ中,排队处理,这已经算是到应用层。这一层主要是削峰,请任务放到队列中,峰值的时候排队处理,不会阻塞
  5. 应用层,处理各种应用。这一层就是高并发模型和异步处理,实例实现并发的多进程,多线程的处理,也就上面购物系统处理的一个流程。
  6. 最后就是DB层,这一层是瓶颈所在,分层的目的是为了将压力留在上层,一般瓶颈都是在数据库,所以需要把有效得请求到数据库中进行处理,针对数据库的瓶颈,主要就是索引读写分离,分区分库分表等。
  7. 在每一层能有缓存的地方都要用缓存,缓存的效率比数据库要高很多,哪怕是缓存数据库。
  8. 在每一层最好能使用集群和分布式来加强系统的承受能力。

整体来说,核心思想就是限流、缓存、削峰、高并发、异步处理,集群和分布式,做好这些模块才能保证系统的稳定性和最大的承受能力,在高并发和大流量的情况下。

当然这些就是属于业务架构了,都是可以使用微服务的架构来实现,每一块都可以做成一个微服务。

限流截流算法

令牌桶算法

令牌桶算法主要用于限制流量的平均流入速率,允许出现一定的突发流量。比如我们常用的nginx就是一个典型的令牌桶算法的实现:

  • 每秒会有r个令牌被放入桶内,也就是说以1/r的速度向桶中放入令牌
  • 桶的容量是固定不变的,所以多出来的令牌就会被丢弃
  • 当一个n字节的请求包到达时,消耗n个令牌,然后才能发送该数据包
  • 如果桶内令牌小于n,数据包就会被限流,比如缓存或者丢弃

实例

nginx限流模块的使用,tengine是在nginx的基础进行二次开发,针对高并发,大流量的场景进行优化,也可以了解一下

#统一在http域中进行配置

 #限制请求:表示每个ip每秒的请求数不能超过50个
 limit_req_zone $uri zone=api_read:20m rate=50r/s;

 #按ip配置一个连接 zone
 limit_conn_zone $binary_remote_addr zone=perip_conn:10m;

 #按server配置一个连接 zone
 limit_conn_zone $server_name zone=perserver_conn:100m;

location / {
 if (!-e $request_filename){
  rewrite ^/(.*) /index.php last;
 }

 #请求限流排队通过 burst代表着桶的大小,默认是0,这里代表缓存100个
 limit_req zone=api_read burst=100;

 #连接数限制,每个IP并发请求为50
 limit_conn perip_conn 50;

 #服务所限制的连接数(即限制了该server并发连接数量)
 limit_conn perserver_conn 200;

 #连接限速
 #limit_rate 100k;

漏斗算法

  • 可以以任何速度向桶内发送请求
  • 当然桶的容量还是固定的,如果请求大于桶了,就会被丢弃
  • 然后按固定过得速度来处理桶内的请求

计数器算法

就是对某个对象在单位时间内允许被操作的次数,一旦超过了所设定的阈值,就会拒绝请求,到时间后重置计数器,重新限制。

比如某个商品在10s内只能抢购5000次,对抢购进行计数,当达到5000次之后,就会拒绝请求,10s过后,对已经抢购的次数进行重置,重新进行抢购。

缓存

使用缓存将频繁访问的热点数据存储在最近的地方(比如CDN),最快响应的地方(比如本地内存),能够快速响应。

本地缓存

优点只有一个,操作本地内存简单,响应快。但是他的缺点很明显,因此适用场景也是很明确的

  • 数据不能共享
  • 数据会丢失
  • 内存一般不大,用尽后会OOM

所以分布式缓存是比如发展的趋势,正常我们都是会将本地缓存和分布式缓存结合使用。

分布式缓存

分布式缓存也就是我们常用的缓存数据库,比如redis,memcache等,具体就不多说了,可以自己去详细了解

同一key的读要求

1、使用redis进行多读多写

就是采用冗余存储的方案,将一个key进行计算加工将其存储到redis集群中的各个节点,这个时候进行查询的时候就可以通过处理key来轮询均衡处理,解决热点商品哪怕使用集群也会出现单点的问题。需要注意数据一致性问题。

2、本地缓存+redis

定时重redis缓存中同步数据到本地缓存,时间随着商品的热点来定,在定时期间可能出现数据不一致的情况,需要在最后的数据库扣除的时候做处理,在redis中使用分布式锁,在mysql中就是使用正常的锁。

还可以通过推送消息的方式来减少不一致的情况,如果redis有变更就推送到队列中,本地消费更新。

3、实时热点系统

我们不可能准确的把所有的热点数据都推送到缓存中,这就需要我们在交易的过程中发现热点数据同步的缓存中,一般通过收集日志进行分析,最后自动推送到redis中。

同一key的写要求

1、在redis中进行扣减库存

还有进行优化,就是合并操作批量一次处理

2、还有用一些优化的数据库,比如ALISQL

削峰

削峰其实就是对峰值流量进行分散处理,避免在同一时间内产生较大的用户流量冲击系统,从而降低系统的负载压力。

分时削峰

将抢购不要设置为一个时间点,比如0点,而是通过多设置几个时间点,来分散用户流量,降低系统压力

验证削峰

通过验证来拦截缓存用户的请求,比如现在做的验证码新高度的12306的验证,虽然各种各样的验证码,但是确实成功的阻挡了秒杀器和降低了峰值流量

队列缓存削峰

将请求放到Q中,按顺序固定速率的处理,实现流量可控,保证系统的稳定,不会奔溃。

异步

异步处理并不算一种并发的使用方式,但是却是并发中经常使用的,在工作池的基础上使用goroutine处理,但是不用等返回,留一个channenl返回,使用select读取channel中的数据,完成处理,这样可以加大处理的速度,也就提高了并发能力。

数据库优化

对于非结构化或者可以设计为非结构化的数据,我们可以放在redis这种缓存数据库中,但是重要的业务数据最终还是需要关系型数据库的保证,所以对于关系型数据库的优化使用,重来都没有停止过。

关系型数据库最常见的瓶颈

  • 大量读写难以承受
  • 大量查询索引,效率低下

解决这些问题,最常见的就是读写分离,分区分库分表。

读写分离

读写分离很简单,一般数据库都是一主一从,主库用于写,从库用于读,可以很大程度上缓存数据库的压力。

当然这样也有问题,就是主从之间存在着数据延迟,一般都是重设计上解决这个问题,在数据落到主库的时候落一份到缓存,避免从库查不到数据。

分区分库分表

设计表的时候就应该有分区,这样可以加快查询,就像设计好的索引一样。

首先就是垂直拆分,也就是分库,将不同类型的数据放到不同的库中,比如不停地方的订单放到不同的库中,每个库的压力就不大了。

然后就是水平拆分,也就是分表,同一个业务表,数据量达到500W,查询效率就很低下了,优化索引什么都没有用,就需要对业务表进行拆分,将将业务按一定的序号拆分为子表,这个时候就需要一些组件完成路由,我们常见的有mysql sharding,mycat等。

其实在业务量再大的情况下,可能需要同一个业务进行分库分表的操作。

基本系统

不管是正常的购物还是抢购还是秒杀都是这么一个基本流程,包含这个一下基本的系统

搜索系统---订单系统---物流系统
订单系统:购物车系统---确认订单---支付系统,核心就是出发数据库修改库存

首页,用户,广告,购物,订单,商品,结算

基本业务场景

秒杀

秒杀系统会做,基本的购物,抢购,抢红包,大促都会做,秒杀的特性:限时限量,导致的场景就是瞬时并发量大。

热场

在活动开始之前,最好设计一个“热场”。

“热场”的形式多种多样,例如:分享活动领优惠券,领秒杀名额等等。“热场”的形式不重要,重要的是通过它获取一些准备信息。

例如:有可能参与的用户数,他们的地域分布,他们感兴趣的商品。为后面的技术架构提供数据支持,可以先对相关数据进行缓存,减少缓存的压力。

千万级秒杀系统架构

我们再具体看一下包含秒杀的逻辑具体架构

秒杀系统设计的核心思想:尽量将请求拦截在上层(限流,削峰),高并发(异步处理),充分使用内存缓存。

整体流程中优化的点

(限流)            1、web层使用js技术,比如购买按钮置灰,可以减少大量的重复请求
(限流截流)        2、负载均衡nginx(也就是网关),进行截流,比如相同请求,相同的IP,只转发一个,分发到不同的服务实例
(缓存)            3、cdn缓存技术,将很多的静态资源存储在cdn中,能够快速返回
                        1、限制用户维度访问频率
                            针对同一个用户( Userid 维度),做页面级别缓存,单元时间内的请求,统一走缓存,返回同一个页面。
                        2、限制商品维度访问频率
                            大量请求同时间段查询同一个商品时,可以做页面级别缓存,不管下回是谁来访问,只要是这个页面就直接返回。
(削峰)            4、然后将请求放到MQ中,排队处理,
(高并发,异步处理)  5、到后台实例实现并发的多进程,多线程的处理,也就上面购物系统处理的一个流程

还有一下业务上的优化,其实就是使用了均衡和削峰填谷的思想,比如12306所做的,分时分段售票,原来统一10点卖票,现在8点,8点半,9点,…每隔半个小时放出一批:将流量摊匀。

1、并发控制

2、并发安全

3、异步处理:定一个channel,以goroutine来运行,用于接受异步返回的结果

4、分布式事务

5、分布式安全

(高并发)           6、最后到数据库包括缓存数据库的读写分离,分区分库分表。
(缓存)             7、缓存是很快的用在每一层能够使用的地方

分层的目的是为了将压力留在上层,一般瓶颈都是在数据库,所以需要把有效得请求到数据库中进行处理

秒杀系统设计的核心思想:限流,削峰,高并发,异步处理,内存缓存