书接上回,继续聊聊“鬼见愁”项目中的那些神操作

数据库相关

1. ES索引过大导致集群分布不均衡

神操作: ES单索引分片数据过大导致集群中数据倾斜,历史数据不归档

单索引分片数据过大或者索引分片数与集群实例数相差较多容易导致数据倾斜,使得集群某个实例负载较高而其他实例负载较低,在集群整体负载不高的情况下出现部分响应耗时较长的现象,也容易出现因为某个实例的存储水位过高导致整个集群不能写入数据的情况。

建议: 提前规划好集群规模与索引分片的大小,避免出现数据倾斜的问题

2. 历史数据不归档,数据无限增长

神操作: 存储资源申请好之后就一直往里写,对于不用的历史数据不删除、不归档

当存储中有较多的历史数据时,随着数据量的增长系统的响应时长增加,若还想要保证一定的性能,那么就得…得加钱!

建议: 在初期要对数据存储规划好,某些场景数据可按时间分片存储,对于历史数据可及时的归档到成本更低的存储中,降低在线存储的压力

3. 数据库连接数过高

神操作: 一个物理数据库实例中创建了几十个逻辑数据库,在服务扩容的时候产生数据库连接数过高的问题

一个物理数据库中有M个逻辑数据库,一个服务部署了N个容器,一个容器中有K个进程,那么对于一个数据库实例来说有M×N×K个连接,连接数过高会增加资源的消耗对数据库来说也是比较危险。如果有Y个服务,那真的是要炸了,绝了嘿!到了这个地步已经是一个比较棘手的问题,服务也不能随便扩容,解决成本很高,在数据库前面加一层proxy可以缓解这个问题但要根本解决问题需要调整整个系统的设计

建议: 单个数据库实例中不应放太多的逻辑数据库,对数据库的连接数量应做好控制。如果是因为性能问题而在一个容器中部署多个进程,应从系统性能测角度去解决问题,不应无脑的在一个容器中开启多个进程

4. 缺乏数据库连接生命周期管理

神操作: 没有数据库连接的管理,执行过程中可能会使用到无效的连接

场景一:通过名字服务解析数据库地址失败导致获取不到IP,将空字符串作为IP传入DBManager导致在真正创建连接去执行SQL时失败

场景二:创建好的连接作为全局变量,当数据库proxy实例扩缩容时连接可能已经失效,在使用保存到全局变量里的连接时也不判断连接是否可用导致操作数据库失败

建议: 尽可能的采用标准库或成熟库实现连接和连接池的管理,不能简单的通过全局变量来保存连接

5. 单表数据量过大

神操作: 单表存放过亿条数据且存在较大的字段,存储大小过T,导致某些查询时间过长

建议: 对表容量大小做好规划,及时归档历史数据或做拆表处理,对于较大字段可考虑用子表存储并进行压缩处理

6. 采用采用GBK、latin1编码,不支持特殊字符

神操作: 数据库编码使用latin1,写入一些特殊字符时会出现乱码

推测是历史问题,早期并没有考虑到字符编码的问题

建议: MySQL使用utf8mb4编码,PostgreSQL使用UTF8编码

7. Redis key不设置过期时间,Redis内存爆炸

神操作: 使用Redis做缓存,有很多key没有设置过期时间且还有新的数据不断的写入,导致Redis内存爆炸

建议: 对Redis中的缓存数据设置有效的过期时间,对于不期望过期的数据应对数据的大小及数据量随着时间的增长做到心中有数,重视存储的监控报警!

监控报警相关

1. 监控系统老旧,关键链路监控缺失

神操作: 系统使用旧的监控平台,随着旧平台的遗弃系统的监控数据开始出现缺失

监控不完善会使得我们对业务的运行情况缺乏掌控力,甚至对外暴露的接口都由谁在调用都不是很清楚,出现线上问题不能快速定位问题。

建议: 完善系统监控,包括基础的系统监控和关键的业务指标监控

2. 关键监控项没有报警或者报警被屏蔽

神操作: 系统中的关键的监控项没有设置报警,甚至部分报警被屏蔽

监控项没有报警则监控作用丧失了一半,不可能天天人肉盯着监控表盘。如果因报警被消息太多而主动屏蔽应该考虑报警阈值设置的准确性和有效性,而不应该直接将其屏蔽,以免错过重要的报警信息。

建议: 报警不应被屏蔽,并且应随着业务的发展要对报警阈值做合适的调整

3. 日志打印Unicode编码的中文

神操作: 日志中打印出的中文为Unicode编码,不能一眼明确日志内容,需要将日志进行Unicode转中文影响工作效率

建议: 日志中的信息如果能用英文描述清楚尽量用英文,如果使用中文则需要注意字符编码问题,日志本是用来排查问题,不应再给排查问题增加其他的负担

系统设计相关

1. 链路不统一,数据一致性难保证

神操作: 涉及到与下游交互的同一业务逻辑有两条不同的链路且都在生效,当下游接到的数据不符合预期时不知是哪条链路出现了问题

在某次case排查的过程中,原本以为只有一条生效链路,在这条链路上与下游一起排查了好久,最后发现还有另外一条链路,并且是另外一条链路bug引发的问题,坑爹至极。

建议: 对于同一个逻辑不应存在多种生效的路径,如果是要对旧的路径做替换应及时下线旧的路径

2. 复杂的业务流程,缺乏补偿机制

神操作: 对于复杂且流程很长的业务逻辑缺少补偿机制,当在流程中某个环节出现失败时则这个内容将彻底无法继续下去,只能通过人工修数据的方式去解决,低效且易出错

建议: 提供自动/手动补偿的机制,以免出现故障时修数据时手忙脚乱

3. 系统重构烂尾,系统间关系混乱

神操作: 系统重构到一半烂尾,一部分逻辑在新系统,一部分逻辑仍然在旧系统中,使得原本就不清晰的调用关系变的更复杂,系统的熵不减反增

建议: 系统重构前应做好技术方案难度、可行性及人力评估,避免出现项目烂尾

4. 依赖的基础设施陈旧

神操作: 系统仍在使用陈旧的基础设施,而这些基础设施都已濒临下线或处于无人维护的状态

建议: 跟随公司基础设施的迭代及时升级,使用陈旧的基础设施将使得系统稳定性更加难以保障

5. 不再使用的功能不维护却也不下线

神操作: 对于一些不再使用的功能不进行下线处理,在意想不到的地方仍会触发旧功能逻辑引发bug

建议: 及时清理下线不使用的功能代码,提升系统的稳定性

总结

在文章中所列举的问题一部分是在开发过程中不细心,没有养成良好的开发习惯所致;一部分是缺乏对系统的维护、缺乏对线上的敬畏之心;甚至还有一部分是非技术原因导致的。

系统中所表现出的问题都是表象,实际背后都是人的问题。对系统中所犯下的错误要引以为戒,以免在后续的工作中犯下相同的错误。