消息模型:主题和队列区别
早期的消息队列,就是按照“队列”的数据结构来设计的。我们一起看下这个图,生产者(Producer)发消息就是入队操作,消费者(Consumer)收消息就是出队也就是删除操作,服务端存放消息的容器自然就称为“队列”。
以下是最初的一种消息模型:队列模型
这种模型中,消费者之间是竞争关系,每个消息只能被一个消费者消费。
但是如果想要一个消息被多个消费者消费,比如说对于一个订单消息,风控系统,分析系统,支付系统都需要得到该订单的信息,那么采用这种模型的话就需要为每一个消费者创建一个队列,但是这样做就需要提前知道有多少个消费者,违背了解耦。
为了解决这个问题,演化出了另外一种消息模型:“发布 - 订阅模型(Publish-Subscribe Pattern)”。
以上两种模型的区别就是,一份消息数据能不能被消费多次的问题。
RabbitMQ 的消息模型rabbitmq采用队列模型,它用了一个交换机来解决多个消费者消费同一条消息的问题。
生产者不关心消息发送给哪个消费者,它只需要发送给交换机,由交换机决定发送给哪个消费者。
RocketMQ 的消息模型RocketMQ 使用的消息模型 ...
分库分表时如何保证id唯一
主键如何选择1、使用业务字段作为主键,比如说对于用户表来说,可以使用手机号,email 或者身份证号作为主键。
2、使用生成的唯一 ID 作为主键。
但是第一种并不是每一张表都可以使用的,一些特殊的字段可以使用,比如说身份证,邮箱,手机号,但是这些字段可能存在变更的情况,就比较麻烦,所以最好采取第二种方案。
第二种方案在单表时,可以采用简单的自增id来实现,但是在分库分表的情况下却并不能这样,因为不同库和不同表的自增id会重复。我们需要采取一些其他的办法来实现。
基于 Snowflake 算法搭建发号器首先一点是,为什么不采用uuid来当作主键 ?
1、id最好是有序的,某些场景会需要排序,如果按照id则效率高一点,而且id有序分区也会简单。
2、id有序时,将其作为主键,插入数据时效率也会高,如果采用无序的插入,可能会频繁的导致页分裂。
3、uuid作为主键可能会占用大量的空间。
4、uuid并不具备业务含义。
Snowflake 算法Snowflake 的核心思想是将 64bit 的二进制数字分成若干部分,每一部分都存储有特定含义的数据,比如说时间戳、机器 ID、序列号等等,最终生 ...
MySQL分库分表
分库分表分库分表是一种常见的数据分片方式,它不同于集群那种完全的备份数据,而是每个数据库或者每张表只存储整个数据的一部分,这样可以保证总数据量不变的情况下,每个数据库和每张表少存储一些数据。
而且在数据写入时,也会变为往一个库或者一张表写变为往多个库或者多张表写,提高并发写入能力。
如何对数据库做垂直拆分垂直拆分就是对数据库竖着拆分,也就是将数据库的表拆分到多个不同的数据库中。
垂直拆分的原则一般是按照业务类型来拆分,核心思想是专库专用,将业务耦合度比较高的表拆分到单独的库中。
如何对数据库做水平拆分水平拆分指的是将单一数据表按照某一种规则拆分到多个数据库和多个数据表中,关注点在数据的特点。
拆分规则1、按照某一个字段的哈希值做拆分,这种拆分规则比较适用于实体表。
比如说我们想把用户表拆分成 16 个库,每个库是 64 张表,那么可以先对用户 ID 做哈希,哈希的目的是将 ID 尽量打散,然后再对 16 取余,这样就得到了分库后的索引值;对 64 取余,就得到了分表后的索引值。
2、按照某一个字段的区间来拆分,比较常用的是时间字段。
分库分表带来的问题分库分表引入的一个最大的问 ...
池化技术
在后端代码操作MySQL时,需要先与MySQL建立连接, 这个连接需要使用TCP的三次握手,比较的耗时,如果在正常的系统中,每一次执行sql,都要建立连接,连接使用完毕后断开连接,那么就会带来严重的性能影响。因此,池化技术就这样诞生了。
连接池基本配置MySQL连接池有两个重要配置,最小连接数和最大连接数。
如果连接池中有空闲的连接,则直接使用这些已经建立好的连接,如果已有的连接数超过最小连接数,但是没有超过最大连接数,来了新的连接请求,则创建新的连接处理请求。如果当前连接数大于等于最大连接数,则让新的请求排队,如果超时则抛出错误。
还有一种情况是,当前连接池中的连接数小于最小连接数,那么新到的连接请求会直接创建新的连接,而不会使用池子中的连接。
针对于连接池中的连接,我们可以启动一个线程定期检测连接池是否可用,如果不可用就关闭该连接。
用线程池预先创建线程jdk1.5中就提供了池化技术。ThreadPoolExecutor 就是其中的一种,它有两个参数coreThreadCount 和 maxThreadCount。
如果线程池中的线程数少于 coreThreadCount 时,处 ...
TCP
TCP是面向连接的。而且提供的是全双工服务,即如果两台主机A和B相连,那么A可以给B发数据,B也可以给A发数据。TCP连接是点对点的,即在单个发送方与单个接收方之间的连接。
建立连接的过程客户首先发送一个特殊的TCP报文段,服务器用另一个特殊的TCP报文段来响应,最后,客户再用第三个特殊报文段作为响应。
前两个报文段不承载“有效载荷” ,也就是不包含应用层数据;而第三个报文段可以承载有效载荷。
数据传输一旦建立了连接之后,就需要传输数据。当应用层数据通过套接字以后,数据就交由TCP控制。TCP将这些数据引导到该连接的发送缓存里,然后不时的从发送缓存里取出一块数据,并将数据传递到网络层。
TCP报文段结构
其中,32比特的序号字段和32比特的确认号字段被TCP发送方和接收方用来实现可靠数据传输服务。
接收窗口字段用来实现流量控制。
TCP被称为累计确认是因为它的确认号也是按组确认的。比如说收到了0-500的报文段,那么它发送给发送方的确认号就是501,如果收到0-500,和600-900,而没有收到501-599,那么确认号还是501。
TCP的发送方,也使用了流水线。
可靠数据传 ...
如何提高系统性能
性能的度量指标1、平均值
平均值是把这段时间所有请求的响应时间数据相加,再除以总请求数。但是它存在一定的问题,可能这段时间有10000个请求,只有100个响应时间为100ms,其他都是1s,这样算下来平均值不太大,但是系统是有问题的。
2、最大值
即找到一段时间请求的响应时间的最大值。但是又过于敏感,如果只有一个请求响应是100ms,那么最大值就是100。
3、分位值
分位值有很多种,比如 90 分位、95 分位、75 分位。以 90 分位为例,我们把这段时间请求的响应时间从小到大排序,假如一共有 100 个请求,那么排在第 90 位的响应时间就是 90 分位值。
高并发下的性能优化加入说现在的系统中只有一个处理核心,执行的响应时间都在10ms以内,我们该如何优化呢?
1. 提高系统的处理核心数当提高了系统的处理核心数,那么我们就可以开更多的线程来同时处理请求。那么系统的吞吐量会变得大一点。但是并不意味着无限制的增加处理核心数可以一直的提高性能。随着并发进程数的增加,并行的任务对于系统资源的争抢也会愈发严重。在某一个临界点上继续增加并发进程数,反而会造成系统性能的下降。
2、减少 ...
临时表为什么可以重名
临时表的一些特性:
1、创建语法:create temporary table
2、只有创建该表的session才可以访问,其他线程无法访问
3、临时表可以与普通表重名
4、session中有同名的临时表和普通表时,操作的都是临时表
临时表应用在分库分表的场景下,我们现在假设某一个大表ht 按照字段f分为了1024个表,分布在32个数据库上。如下图所示:
这种设计下,如果我们执行如下sql:
1select v from ht where f = N;
因为该sql使用了分表的字段f,那么我们就可以直接找到数据所在的表。
但是如果是如下sql:
1select v from ht where k >= M order by t_modified desc limit 100;
该sql没有使用到分表的字段f,那么只能到所有的分区中去查找满足条件的所有行,然后统一做order by 的操作。
这种情况下,有两种方法:
1、把所有的数据全部查到,然后交给代理层去做排序。这种方法的优点是MySQL查询会很快,但是需要代理层多做额外的处理。
2、把各个分库拿到的数据,汇总到一个 ...
流水线可靠数据传输协议
rdt3.0虽然是一个正确的协议,但是很多人对于它的性能并不是很满意,因为它基于停等协议。而且是发送一个报文,就需要等待对方回复后才可以发送下一个,这很大程度上会影响性能。
对此最简单的解决办法是,发送一个报文段后并不等待回复,而是继续发送,累计N个报文段后再等待对方回复,这样效率就会提高。如下图所示:
这种技术也被称为流水线。有如下要求:
1、必须增加序号范围,而且每个分组有一个唯一的序号,用于确认是否正确传输。
2、协议的发送方和接收方都需要缓存多个分组,发送方需最低要缓存已经发送的但是对方还没确认的报文,用于重新发送。
3、解决流水线的差错恢复有两种基本方法:回退N步和选择重传
回退N步(GBN)该协议中,允许发送方发送N个分组而不需要等待确认。这里的N一般称为窗口长度,如果窗口里面的数据都没有被确认,则无法继续发送,如果有数据确认,则窗口向前移动,继续发送数据。如下图所示:
该协议也被称为滑动窗口协议。该协议的发送方必须响应以下三类事件:
1、上层的调用。当上层调用时,发送方先检查窗口是否已满,即是否有N个数据未确认。如果没满,就产生分组并且发送,并更新变量。如果满 ...
MySQL读写分离存在的问题
在MySQL的一主多从的架构中,往往有以下两种设计方案:
1、由客户端决定连接哪个数据库
2、由代理决定请求分发到哪一个数据库
但不管是哪种方案,都存在过期读的问题,即主库和从库存在一定的时延,用户刚做一个修改,然后立马发起查询,就有可能查到过期数据。以下给出几种解决方案。
强制走主库该方案将请求分为了两类:
1、必须拿到最新数据的,就强制走主库查询。
2、对于可以读取到旧数据的,就走从库查询。
Sleep 方案该方案的设计很简单,读从库之前先sleep一段时间。
它假设大多情况下主备延迟在1秒之内,所以简单的sleep可以拿到最新的数据。
判断主备无延迟方案这里有几种办法,第一种是从库查询前,先判断seconds_behind_master是否已经等于0。如果还不等于0 ,那就必须等到这个参数变为0才能执行查询请求。
第二种和第三种方案,都是通过对比主库和从库的日志执行位点来判断是否有延迟,即通过对比主库和从库执行的日志,来判断,要比对比时间准确。
但是这里也存在问题,主库存在一部分日志刚刚提交,而从库还没收到该日志,也会导致有一定的延迟。
配合semi-sync这里引入了半 ...
高并发系统的通用设计方案
高并发系统的通用设计:
Scale-out(横向扩展):采用分布式部署的方式把流量分流开,让每个服务器都承担一部分并发和流量。
缓存:使用缓存来提高系统的性能,就好比用“拓宽河道”的方式抵抗高并发大流量的冲击。
异步:在某些场景下,未处理完成之前我们可以让请求先返回,在数据准备好之后再通知请求方,这样可以在单位时间内处理更多的请求。
Scale-out这里牵扯到一个横向扩展(Scale-out)与纵向扩展(Scale-up)。其中纵向扩展是不断的提高单个cpu的处理能力,来处理更多的请求。而横向扩展则是指利用多个cpu资源来并行处理,来处理更多请求。
一般来说,在项目初期,我们可以采用纵向扩展,当单机无法承受时,再使用横向扩展。
缓存我们的数据都是存储在磁盘上的,而磁盘的读取速度特别的慢,会给处理请求带来很大的压力。所以产生了缓存。在现代的设计中,从操作系统到浏览器,从数据库到消息队列都可以看到缓存的影子。
由于缓存是基于内存读写的,所以速度要比磁盘读取快很多。如果能够能快的读写数据,那么每个请求的处理时间都会变短,那么就会提高系统的并发度。
异步处理异步处理相对应的就是同步。
同步是 ...