概念一致性

  • 在系统设计中,概念完整性应该是最重要的考虑因素

大多数统体现出的概念差异和不一致性的原因: 并不是因为它由不同的设计师们开发,是由于设计被分成了由若干人完成的若干任务

概念的完整性要求: 系统只反映唯一的设计理念,用户所见的技术说明来自少数人的思想

为了连贯的设计思路,宁可省略一些不规则的特性和改进,也不提倡独立和无法整合的系统,哪怕包含着许多很好的设计

获得概念的完整性

  • 系统的实现目标:功能性,易用性,功能与理解上复杂程度的比值才是系统设计的最终测试标准
  • 简洁和直白来自概念的完整性
  • 易用性需要设计的一致性和概念上的完整性

每个部分必须反映相同的原理、原则和一致的折衷机制。在语法上, 每个部分应使用相同的技巧;在语义上,应具有同样的相似性

贵族专制统治和民主政治

系统的概念完整性

  • 要求设计必须由一个人,或者非常少数互有默契的人员来实现
  • 决定了使用的容易程度

不能与系统基本概念进行整合的想法和特色,最好放到一边不予考虑。如果出现了很多非常重要但不兼容的构想,就应该抛弃原来的设计,对不同基本概念进行合并,在合并后的系统上重新开始

系统的结构师(产品)

  • 用户的代理人,支持用户的真正利益,而不是维护销售人员所鼓吹的利益
  • 易用性很大程度上依赖结构师

结构师工作产物的生命周期比那些实现人员的产物要长,并且结构师一直处在解决用户问题,实现用户利益的核心地位。如果要得到系统概念上的完整性,那么必须控制这些概念

实现人员

  • 产品的成本性能比很大程度上依靠实现人员

在给定体系结构下的设计实现,同样需要创造性、同样新的思路和卓越的才华

纪律和规则

  • 最差的建筑往往是那些预算远远超过起始目标的项目
  • 外部的体系结构规定实际上是增强,而不是限制实现小组的创造性
  • 创造性活动会因为规范化而得到增强

一旦将注意力集中在没有人解决过的问题上,创意就开始奔涌而出。在毫无限制的实现小组中,在进行结构上的决策时,会出现大量的想法和争议,对具体实现的关注反而会比较少

在等待时,实现人员应该做什么

  • 不能让实现队伍来负责产品设计: 进度推迟,质量更加低劣: 概念完整性的缺乏导致系统开发和 修改上要付出更昂贵的代价

工作的垂直划分(工作被划分成体系结构、设计实现和物理实现)从根本上大大减少了劳动量,结果是使交流彻底地简化,概念完整性得到大幅提高

效率高和效率低的实施者之间具体差别非常大,经常达到了数量级的水平

需要协作沟通的人员的数量影响着开发成本,因为成本的主要组成部分是相互的沟通和交流,以及更正沟通不当所引起的不良结果(系统调试)

队伍以类似外科手术的方式组建,而并非一拥而上由一个人来进行问题的分解,其他人 给予他所需要的支持,以提高效率和生产力

成员分类

首席程序员(外科医生)

对整个业务有完整清楚的理解,负责整体的设计、规范、框架的编码和功能划分,对复杂的模块亲自编码

一个人开发应该很难存在,还有很多scut work需要普通程序员完成,
定义功能和性能技术说明书,设计程序,编制源代码、测试以及书写技术文档。

首席程序员需要极高的天分、丰富的经验和应用数学、业务数据处理或者其他方面的大量系统知识和应用知识。

副手

首席程序员的后备,充当外科医生的保险机制,只是经验较少。

帮助首席程序员思考、讨论和评估,能代表自己的小组与其他团队讨论有关功能和接口问题,了解所有的代码

普通程序员

根据首席程序员的任务划分,按既定规则开发指定的功能模块,提供规范的输入输出参数

管理员

控制财务、人员、工作地点和办公设备,负责与组织中其他管理机构的交流,可以为多个团队服务

编辑

很难存在工作枯燥
提供各种参考信息和书目,对许多个版本进行维护,并监督文档的生成机制
框架、整体的文档由首席程序员给出,各个接口模块由响应的开发给出

两个文秘(不明确)

管理员和编辑每个人需要一个文秘。
管理员的文秘负责非产品文件和使项目协作一致。

程序职员(不明确)

他负责维护编程产品数据库中所有的团队技术记录。该职员接受文秘性质的培训,承担机器码文件和可读文件的相关管理责任。
他还负责记录和更新所有小组成员的工作拷贝,并使用自己的交互式工具来控制产品逐步增长的完整性和有效性。

工具维护人员(运维,组件组)

保证所有基本服务的可靠性,以及承担团队成员所需要的特殊工具的构建、维护和升级责任,常常要开发一些实用程序,编制具有目录的函数库以及宏库。

测试人员

负责计划测试的步骤和为单元测试搭建测试平台

语言专家

寻找一种简洁、有效的使用语言的方法来解决复杂、晦涩或者是棘手的问题,比如DBA
他通常需要对技术进行一些研究(2-3天)。通常一个语言专家可以为2-3个首席程序员服务。

如何运作

系统是一个人或者最多两个人思考的产物,因此客观上达到了概念的一致性

外科手术团队中,不存在利益的差别,观点的不一致由外科医生单方面来统一

让 200 人去解决问题,而仅仅需要 协调 20 个人,即那些“外科医生”的思路

乐观主义

创造性活动分为三个阶段: 构思、实现和交流

只有在实现的过程中,才能发现构思的不完整性和不一致性

由于编程的高可控性,我们会认为实现很顺利,因此造成了乐观主义的弥漫,而构思是有缺陷的,因此总会有bug

大型的编程工作,或多或少包含了很多任务,某些任务间还具有前后的次序,从而一切正常的概率变得非常小,甚至接近于无

人月

用人月作为衡量一项工作的规模是一个危险和带有欺骗性的神话

编程中近乎不可能存在人员数量和时间可以相互替换

  • 任务次序上的限制不能分解

Resize icon

  • 子任务之间需要相互沟通和交流的任务,沟通所增加的负担由两个部分组成,培训和相互交流,培训随人员的数量呈线性变化,相互之间交流按照 n(n-1)/2 递增

Resize icon

所增加的用于沟通的工作量可能会完全抵消对原有任务分解所产生的作用,甚至会导致延长

系统测试

项目排期占比

  • 1/3 计划
  • 1/6 编码
  • 1/4 构件测试和早期系统测试
  • 1/4 系统测试,所有的构件已完成

很少项目允许为测试分配一半的时间,但大多数项目的测试实际上是花费了进度中一半的时间

不为系统测试安排足够的时间简直就是一场灾难因为延迟会发生在项目快完成的时候,坏消息没有任何预兆,很晚才出现,此时的延迟具有不寻常的、严重的财务和心理上的反应,甚至会导致活动延误,付出相当高的商业代价,远远高于其他开销。因此,在早期进度策划时,允许充分的系统测试时间是非常重要的

空泛的估算

现状: 受限于顾客要求的紧迫程度,非阶段化方法的采用,少得可怜的数据支持,加上完全借助软件经理的直觉

解决方案:

  • 开发并推行生产率图表、缺陷率、估算规则等
  • 在基于可靠基础的估算出现之前,项目经理需要挺直腰杆,坚持他们的估计, 确信自己的经验和直觉总比从期望派生出的结果要强得多

重复产生的进度灾难

避免小的偏差(Take no small slips) 在新的进度安排中分配充分的时间,以确保工作能仔细、彻底地完成,从而无需重新确定时间进度表

重复生成的工作量 不论在多短的时间内,聘请到多么能干的n个新员工,都需要接受一位有经验的职员的培训。如果培训需要一个月的时间,那么n+1个人月会投入到原有进度安排以外的工作中。原先划分的任务要重新拆分某些已经完成的工作必定会丢失,系统测试必须被延长

向进度落后的项目中增加人手,只会使进度更加落后。(Adding manpower to a late software project makes it later)

项目的时间估算 任务顺序和子任务的数量产生的人员数量,推算出进度时间表,该表安排的人员较少,花费的时间较长(唯一的风险是产品可能会过时)。分派较多的人手,计划较短的时间,将无法得到可行的进度表

在众多软件项目中,缺乏合理的时间进度是造成项目滞后的最主要原因,它比其他所有因素加起来的影响还要大

一个问题单独看起来很没有困难,但是当它们相互纠缠和累积在一起的时候,团队的行动就会变得越来越慢

Resize icon

职业的苦恼

  1. 学习编程的最困难部分,是将做事的方式往追求完美的方向调整
  2. 由他人来设定目标,供给资源,提供信息,很少能控制工作环境和目标。个人的权威和他所承担的责任是不相配的
  3. 依赖他人的程序是一件非常痛苦的事情。往往这些程序设计得并不合理,实现拙劣,发布不完整(没有源代码或测试用例), 或者文档记录得很糟
  4. 寻找琐碎的 bug,调试和查错往往是线性收敛的,甚至具有二次方的复杂度。寻找最后一个错误比第一个错误将花费更多的时间
  5. 产品陈旧问题,实现落后与否的判断应根据其它已有的系统,而不是未实现的概念。在现有的时间和有效的资源范围内,寻找解决实际问题的切实可行方案

rpc

使用rpc的目的: 让远程调用服务就像调本地服务一样简单

比http的优势:协议传输效率较低

sofarpc与grpc

性能: sofarpc好,grpc性能一般(单线程 30kqps,小于8k包时)
易用性: Http2出现较晚,迁移转换成本高,sofarpc支持的协议多
可行性: sofarpc的注册中心目前还没有正式支持SOFAMesh

服务发现

zookeeper

  1. 健康检查依赖TCP长链接活性探测不准确
  2. 需要在服务中集成sdk
  3. 更复杂的服务治理

参考

sofa-rpc-node: 目前只支持zk

k8s service

服务注册和发现,转发,负载均衡能力

缺点: 同一个应用的不同的实例提供了不同的服务会出错,只能在同集群内使用

istio

弥补k8s service的不足
可配置的服务发现(从k8s获取服务注册表),由Sidecar保持长连接,转发,负载并提供熔断、限流降级、调用链治理等能力

SOFAMesh

基于istio,还在测试中

Resize icon

  • 采用 Golang 编写的 MOSN 取代 Envoy(sidecar)
  • 合并 Mixer 到数据平面以解决性能瓶颈
  • 增强 Pilot 以实现更灵活的服务发现机制(主动告诉对应的 Sidecar,它需要发布哪些服务,主动告诉对应的 Sidecar,它需要订阅哪些服务),增加数据同步模块,以实现多个服务注册中心之间的数据交换

事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在MySQL中,事务支持是在引擎层实现的

ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)

InnoDB里面每个事务有一个唯一的事务ID,叫作transaction id。它是在事务开始的时候向InnoDB的事务系统申请的,是按申请顺序严格递增的

隔离性与隔离级别

读未提交(read uncommitted) 一个事务还没提交时,它做的变更就能被别的事务看到

读提交(read committed)一个事务提交之后,它做的变更才会被其他事务看到,查询只承认在语句启动前就已经提交完成的数据

可重复读(repeatable read) 一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的,未提交变更对其他事务也是不可见的,查询只承认在事务启动前就已经提交完成的数据

串行化(serializable )同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行

长事务和回滚日志

回滚日志: 在MySQL中,每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值

在查询这条记录的时候,不同时刻启动的事务会有不同的read-view,要获取旧的视图,必须将当前值依次执行回滚操作得到

当没有事务再需要用到这些回滚日志时,就是当系统里没有比这个回滚日志更早的read-view的时候,回滚日志会被删除

长事务的影响系统里面会存在很老的事务视图,在这个事务提交之前数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间

set autocommit=0 这个命令会将这个线程的自动提交关掉,只执行一个select语句,这个事务就启动了,而且并不会自动提交。持续存在直到你主动执行commit 或 rollback 语句,或者断开连接

commit work and chain 提交事务并自动启动下一个事务,省去了再次执行begin语句的开销

查找持续时间超过60s的事务

1
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

SET MAX_EXECUTION_TIME命令,来控制每个语句执行的最长时间,避免单个语句意外执行太长时间

监控 information_schema.Innodb_trx表,设置长事务阈值,超过就报警/或者kill

设置innodb_undo_tablespaces(默认值为0,表示不独立设置undo的tablespace,默认记录到ibdata系统表空间中,否则,则在undo目录下创建这么多个undo文件)成2(或更大的值),如果真的出现大事务导致回滚段过大方便清理

可重复读和MVCC

MVCC 数据表中的一行记录,其实可能有多个版本(row),每个版本有自己的row trx_id,旧版本需要根据当前版本和undo log计算出来

begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作InnoDB表的语句(第一个快照读语句),事务才真正启动。如果想要马上启动一个事务,可以使用start transaction with consistent snapshot1

InnoDB为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在“活跃”的所有事务ID。“活跃”指的就是,启动了但还没提交,数组里面事务ID的最小值记为低水位,当前系统里面已经创建过的事务ID的最大值加1记为高水位。

这个视图数组和高水位,就组成了当前事务的一致性视图(read-view),只有版本已提交,而且是在视图创建前提交的才可见

更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read) set k=k+1会拿最新的值做更新,更新后的trx_id是自己的更新,可以直接使用,如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待

redo log

InnoDB引擎实现

在MySQL里也有这个问题,如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程IO成本、查找成本都很高

WAL技术,WAL的全称是Write-Ahead Logging,它的关键点就是先写日志,再写磁盘 顺序i/o

当有一条记录需要更新的时候,InnoDB引擎就会先把记录写到redo log里面,并更新内存,这个时候更新就算完成了。同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面

redo log是物理日志,记录的是“在某个数据页上做了什么修改”

InnoDB的redo log是固定大小的,比如可以配置为一组4个文件,每个文件的大小是1GB,总共就可以记录4GB的操作。从头开始写,写到末尾就又回到开头循环写。write pos是当前记录的位置,checkpoint是当前要擦除的位置也是往后推移并且循环的,擦除记录前要把记录更新到数据文件

有了redo log,InnoDB就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe

bin log

Server层日志

binlog存在的原因 redolog只有InnoDB有,别的引擎没有,edolog是循环写的,不持久保存,binlog的“归档”这个功能,redolog是不具备的。

binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”,binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志

  • statement:基于SQL语句的模式,某些语句中含有一些函数,例如 UUID NOW 等在复制过程可能导致数据不一致甚至出错。
  • row:基于行的模式,记录的是行的变化,很安全。但是 binlog 的磁盘占用会比其他两种模式大很多,在一些大表中清除大量数据时在 binlog 中会生成很多条语句,可能导致从库延迟变大。
  • mixed:混合模式,根据语句来选用是 statement 还是 row 模式。

两阶段提交

由于redo log和binlog是两个独立的逻辑,如果不用两阶段提交,数据库的状态就有可能和用它的日志恢复出来的库的状态不一致。

引擎将新数据更新到内存中,同时将更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务

执行器生成操作的binlog,并把binlog写入磁盘

行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成

undo log

每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值

当没有事务再需要用到这些回滚日志时,回滚日志会被删除,长事务会导致保存很多回滚日志占用大量存储空间

当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为“脏页”。内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为“干净页”

刷脏页的时间

第一种是“redo log写满了,要flush脏页” 整个系统就不能再接受更新了,所有的更新都必须堵住。如果你从监控上看,这时候更新数会跌为0

第二种是“内存不够用了,要先将脏页写到磁盘” 这种情况是常态

InnoDB用缓冲池(buffer pool)管理内存,缓冲池中的内存页有三种状态:

  1. 还没有使用的;
  2. 使用了并且是干净页;
  3. 使用了并且是脏页。

InnoDB的策略是尽量使用内存,因此对于一个长时间运行的库来说,未被使用的页面很少。

当要读入的数据页没有在内存的时候,就必须到缓冲池中申请一个数据页。这时候只能把最久不使用的数据页从内存中淘汰掉:如果要淘汰的是一个干净页,就直接释放出来复用;但如果是脏页呢,就必须将脏页先刷到磁盘,变成干净页后才能复用

一个查询要淘汰的脏页个数太多,会导致查询的响应时间明显变长;

第三种是MySQL空闲时

第四种是MySQL正常关闭

刷脏页的控制策略

innodb_io_capacity 它会告诉InnoDB你的磁盘能力 设置成磁盘的IOPS

innodb_max_dirty_pages_pct脏页比例上限,默认值是75% 通过Innodb_buffer_pool_pages_dirty/Innodb_buffer_pool_pages_total得到的

InnoDB的刷盘速度就是要参考这两个因素:一个是脏页比例,一个是redo log写盘速度

刷脏页的连坐”机制

一旦一个查询请求需要在执行过程中先flush掉一个脏页时,这个查询就可能要比平时慢了。而MySQL中的一个机制,可能让你的查询会更慢:在准备刷一个脏页的时候,如果这个数据页旁边的数据页刚好是脏页,就会把这个“邻居”也带着一起刷掉;而且这个把“邻居”拖下水的逻辑还可以继续蔓延,也就是对于每个邻居数据页,如果跟它相邻的数据页也还是脏页的话,也会被放到一起刷

innodb_flush_neighbors 值为1的时候会有上述的“连坐”机制,值为0时表示不找邻居,自己刷自己的

找“邻居”这个优化在机械硬盘时代是很有意义的,可以减少很多随机IO。机械硬盘的随机IOPS一般只有几百,相同的逻辑操作减少随机IO就意味着系统性能的大幅度提升

使用的是SSD这类IOPS比较高的设备的话,建议把innodb_flush_neighbors的值设置成0

在MySQL 8.0中,innodb_flush_neighbors参数的默认值已经是0了

broker

ons 默认有两个brokerName组,分别有0-7共16个队列

还有两个死信队列 topic是 %RETRY%+tag

consumer15分钟消费超时,broker会重发消息

Producer

轮询发送到broker队列

如果发送失败会换个队列

defaultTopicQueueNums

自动创建topic时设置队列数量
ons不允许自动创建topic,也不允许修改队列数量

Consumer

AllocateMessageQueueAveragely

  • 默认 集群消费

平均分配可消费的 Broker 对应的消息队列 c1对应b1,b2,b3 c2对应d1,d2,d3

AllocateMessageQueueAveragelyByCircle

以环状轮流分Broker

广播消费

每个Consume都订阅每个Broker

注意点

  1. 刚启动时,broker分配还没有完成,会导致消息重复消费
  2. 但是可能会存在同样的消息有两个不同 msgId的情况(有多种原因),这种情况可能会使业务上重复消费,建议最好使用消息内容中的唯一标识字段去重

由filebeat导入的default template导致

检查

先创建个新的索引:

1
PUT /my_index

然后查下是否有default mapping

1
2
3
4
5
6
7
8
GET /my_index
//像这样
{
"mappings": {
"_default_": {}
}
}

比较下和之前数据的mapping有没有冲突

解决方案

首先查看es中存在的模板 /_template, 是否存在名为filebeat,存在就执行

1
DELETE /_template/filebeat

在日志系统中通常直接由es按天生成索引,这样就简单了,直接把使用错误索引的数据删掉重新导入就好了。

但在使用自己的索引时,就需要做重索引,可以尝试通过索引别名的方式切换

起因

在尝试新的日志格式时,启动了其他版本的filebeat并让它直接output到es,忘记设置 template.enabled: false 导致它导入default模板

在第二天生成新索引时发现kibana报warning shards failed,才发现mapping conflict

填完一坑,继续logstash挖坑