简栈文化

拥抱AI,继续技术成长

背景

前段时间做了很多慢SQL的优化工作,这周刚好又被反馈服务出现了死锁导致了业务报错。看了一下云数据库的告警日志,发现出现了比较多的事务未提交死锁等待行锁的严重警告。都是一些棘手的运维工作,涉及到业务流程的梳理、SQL的优化等工作。

今天趁这个机会,我们一起看下如何去分析这些问题,主要看下等待行锁、死锁。

数据库有哪几种锁?

每次说数据库锁,感觉一大堆。其实如果按照一定的纬度去整理下,还是比较清晰的。如图:

力度划分:表锁、页锁、行锁

算法划分:Record LockGap LockNext-key Lock

实现机制:乐观锁、悲观锁

兼容性:排它锁、共享锁、意向锁

阅读全文 »

背景

我们来先看一个图,了解一下故()事()的背景:

http://static.cyblogs.com/Jietu20211113-162059.jpg

2个跑批任务,其实做的事情是同一件事情,都是为了跟下游系统保持数据的一致性。大任务是每个2h跑一次,小任务是每隔10mins跑一次。除了这2个定时任务以外,还有一个额外的监控任务来做类似的对账,如果发现出现对账不平,就会出现邮件/短信告警到相关的责任上。

这是一个非常有特点的定时任务跑批任务+监控告警的场景了。

从上面的场景上看,我们可以得出一些结论:为了保证一致性写了大小Job来保证,并且还给出了监控告警,说明数据的重要性是比较强的。

某天,出现了频繁的告警提示,每10分钟就告警一次,而且内容没有发生变化,说明同步的index没有变化过。

错误排查

任务有在正常的执行吗?

第一反应肯定是在思考,我的大任务与小任务都有正常执行吗?因为之前的都是正常的。上午看了一下日志与进程发现有在跑,还跑除了多次任务,日志打印不明确,看不到具体分支的逻辑。总结一下问题点:

阅读全文 »

前言

现在对于一个开发来说,Docker应该是再熟悉不过了。 还记得在2013~2014左右的时候,听说多最多的就是Cloud Foundry,那个时候就一直在说云的事情。后面Docker就绝杀了它~

那它帮我们解决了一个什么问题了?面试的时候也许会问到。

在很久以前,我们开发代码,估计最蛋疼的事情就是发布版本了。我还记得在房多多的时候(2014~2016)左右,每次发布几个开发围绕在运维的身边,有时候运维忙不过来,开发就直接在运维的电脑上开始VIM干活了,修改若干配置。由于多环境的原因,我们无法保证每个环境都是一样的。

  • 可能你的操作系统不同,导致打包、发布的脚本不同
  • 环境不同,没有很好的配置管理,你的代码有不同的写法
  • 特别是跟操作系统相关的那些参数,可能瞬间就会带来性能问题

那么Docker就可以把我们的操作系统、代码、脚本等都一起打包成一个Image,就可以保证只要是运行同一个Image,我们的所有内容都是一样的。就不会出现,我在测试环境跑的好好的,一到生产连启动都成问题。

问题

现在一般一个POD就只跑一个进程,DevOps会根据我们的发布流水线自动的将一个项目进行打包、发布,整套的CI、CD做的是行云流水。但是,每个项目ROOT下都会需要一个叫Dockerfile的文件。但偏偏有一些历史项目,没有Dockerfile文件,只有一个Run的容器再跑,真的是非常惊悚。docker rm [OPTIONS] CONTAINER [CONTAINER...],就GAME OVER了。

怎么办?

方法1:以当前容器作为基础镜像
阅读全文 »

背景

之前在思考双活/多活架构的时候,其实对于蓝绿发布是有一些了解的,也梳理过在底层存储是一份,服务是多份的模式有做过深入的分析。但那个时候对于Kubernetes的了解还不是很熟悉,是通过传统的方式来考量的。

因为现在的互联网公司基本都是上云了,我们也必须对于Kubernetes那一整套要有比较深入、熟悉的运用才能真的提高我们的效率。先聊一下,我为什么需要利用灰度+蓝绿发布的模式来去做?

现在有一个比较老的项目,应该在10年+,每天请求量大概在1.5亿+,峰值的QPS在6000/s,存在着比较多性能问题。现在需要在它上面新增一个服务,为了后面优化做准备,比如:请求的分流、限流、熔断、日志的上报与监控(新)、统一编译处理,特殊报文转换等。也就是说,只要你新增加了一层,你才有可能更好的去做更多的事情。

那么我们需要达到一些什么的基础条件了?

  • 服务流量比较大,我们需要对新服务的可靠性需要验证,需要灰度先了解
  • 因为存在慢查询,不能在滚动发布中,导致请求还未执行完毕,就被k8s kill掉了,业务会感知到502

如果是你?针对于这2个基础的要求,你会如何去思考的你架构方案呢?

思考

新增服务的思考:

  • 它的性能必须要强、服务稳定。一个服务的性能好不好,其实跟它的:I/O模型、线程模型、数据结构、算法等息息相关。比如:你在思考Redis单线程为什么快的时候?应该就很能get到这里的点了。解决这个问题,我们选择了Go语言来开发(当然,最熟悉的语言风险最小),为了保证性能,也是做了2轮非常细致的压测。
  • 发布过程中不能因为kill掉服务导致请求502。如果说我在发布的过程中,我把滚动这一步省略掉,直接先准备好一份最新的,验证可以后,我一刀直接把流量引导最新服务上,老的服务也不会断掉,这是否就可以达到效果了?
阅读全文 »

我们在新做项目的时候,需要对我们的服务有有一些性能指标,比如:SLA(需要达到多少个9)、QPSTPS等。因为这些量化的数字让我们更加的了解我们的系统。

我们如何压测?其实个人觉得有2种场景。

第一种:是我们明确的知道目标,看我们通过大量的并发看我们是否有达到。如果没有达到,我们需要通过水平扩容、性能优化等让其达到。

第二种:是我们不知道目标,通过压测可以知道一个固定配置下的单机单服务的最大性能,让我们对它有一个彻底的认识。为后面的目标做更多的铺垫与准备,或者跟行业水平对比,看看差距有多少。

如何用wrk进行压测?

Github地址:https://github.com/wg/wrk,该项目也是开源项目,关注的人还不少,有30.4K。咨询了一下身边的同事,使用它的人还不少。主要的语言的是C语言。

http://static.cyblogs.com/Jietu20211023-153923.jpg

安装
1
2
3
4
5
6
git clone https://github.com/wg/wrk

make

-- 拷贝wrk到bin
cp wrk /usr/sbin/wrk
压测脚本
阅读全文 »

背景

项目中开始用Go,最近写了一下Demo,发现语法还是非常好用,大部分比Java还是简洁很多,也有一些很细节的约定。比如:

  • 字母大小写控制是全包还是本包内访问
  • 变量定义了就一定要使用,否则就会编译不通过等等

更好的就是方法可以返回多个值,这个跟Java比较就是减少很多的封装。因为Go的线程模型特点,用来写一些需要高并发、高性能的项目还是非常好的。所以,趁这个机会也好好的深入了解下。现在也是把PythonPHPGo等都学习一遍,每种语言都有它的优缺点,其实都还挺不错的。

针对于Go语言里:&*的区别,什么时候该用什么做一个总结。

指针

我们经常会听到别人说Go是值传递,某某某是引用传递,某某某是指针传递,等等各种各样的说法。

那么首先他们的区别是什么呢?什么是指针?指针其实也是一个变量,只不过这个变量里面存的不是intfloatstruct,而是一个地址address,然后在这个address上所存储的数据可以通过指针来被阅读到。

OK,指针变量存储的是一个地址,地址从哪里来的?那就得问一个变量的地址怎么取得呢?在变量前面加上一个&符号就行。

好的,指针变量存储了这个地址了,那这个地址所存储的值怎么被阅读到呢?也就是指针所指向的值怎么拿到呢?在指针变量前面加上一个*符号就行。

阅读全文 »

背景

如果说你的数据量并发量不大,或者你的数据量很少没有到千万级别,也许pt-oscgh-osc,online-ddl这些工具都用不着。但是,如果你的数据量很大,数据又很热。如果你没有这些工具,你可能无法完成对一个数据库新增一个字段或者任何一个简单的DDL语句。

简单的分析一下,为了保证数据一致性问题,我们在哪儿都会遇到锁的问题,锁是用来保证顺序性的。谁先拥有锁,谁就可以先执行。锁也会存在力度问题,它跟你要做的一件事情息息相关,我们也会在性能上去做取舍,所有就好了行锁、表锁等。

Waiting for table metadata lock

说一下我遇到的这个场景,数据量数据大概在800W左右,但是表非常的热,长事务也很多。当我要对一个表新增字段的时候,这个时候如果你经验不够足,可能就会“量成大祸”。一般在做DDL会出现:Waiting for table metadata lock。

如果长时间获取不到锁的话,就出现一个可怕的情况:

  • 如果前面的事务未提交,当前是获取不到锁,就不可以执行DDL语句
  • DDL语句未执行之前,后面的请求全部是被hold住的

这样子就会导致一前一后同时夹击,导致整个业务不可用。那么出现Waiting for table metadata lock可能是由哪些原因导致的?

场景一:长事物运行,阻塞DDL,继而阻塞所有同表的后续操作

通过show processlist可以看到TableA上有正在进行的操作(包括读),此时alter table语句无法获取到metadata 独占锁,会进行等待。

阅读全文 »

代码分支管理

大家在做微服务拆分后,难免会导致Application项目以及一些二房包的数量加剧,10+个项目我想应该是很容易的超过。然后这些细粒度的拆分后就会导致发布版本时候的麻烦。

展示一下现阶段我们的一个git的分支流程图,仅供参考。

http://static.cyblogs.com/git_flow_publish_20210415.jpg

简单说明一下:

  • dev环境每次都是从master拉取一个分支,取名为dev-{发布日期}-{sequenceId}sequenceId01开始叠加,避免一个版本需要反复拉取多次。
  • 到了test阶段,也是为了收敛(前期严格一点)。提测的时候代码需要合并到test分支来。
  • UAT阶段,还是需要从master拉取分支,如果出现要重新拉取分支的情况下,还是严格拉取master分支的代码。主要是为了与master保持一致,避免把别人的覆盖掉。取名为release-{发布日期}-{sequenceId},规则同上。
  • 上生产后,验证完毕后需要把代码合并到master分支,并且打包tag

但是在这个过程中,需要有拉取新分支,合并分支,批量删除分支,打tag等等繁琐的操作,项目一多如果有一个批量脚本就更好了。下面我就列举一些平时使用最多的几个,仅供参考:

批量脚本

01-批量拉取新分支

比如一个新的迭代要开始了,就需要从master拉取dev分支。

阅读全文 »

背景

相信很多程序员对于 Linux 系统都不陌生,即使自己的日常开发机器不是 Linux,那么线上服务器也大部分都是的,所以,掌握常用的 Linux 命令也是程序员必备的技能。

但是,怕就怕很多人对于部分命令只是一知半解,使用不当就能导致线上故障。

前段时间,我们的线上应用报警,频繁 FGC,需要紧急处理问题,于是有同事去线上重启机器(正常程序应该是先采集堆 dump,然后再重启,方便排查是否存在内存泄露等问题)。

但是在重启过程中,同事发现正常的重启命令应用无反应,然后尝试使用 kill 命令 “杀” 掉 Java 进程,但是仍然无效。于是他私自决定使用 “kill -9“ 结束了进程的生命。

虽然应用进程被干掉了,但是随之而来带来了很多问题,首先是上游系统突然发生大量报警,对应开发找过来说调用我们的 RPC 服务无响应,频繁超时。

后来,我们又发现系统中存在部分脏数据,有些在同一个事务中需要完整更新的数据,只更新了一半…

为什么正常的 kill 无法 “杀掉” 进程,而 kill -9 就可以?为什么 kill -9 会引发这一连串连锁反应?正常的 kill 执行时,JVM 会如何处理的呢?

要搞清楚这些问题,我们要先从 kill 命令说起。

kill 命令

阅读全文 »

一、Kafka消费者组是什么?

Consumer GroupKafka提供的可扩展且具有容错性的消费者机制。在组内多个消费者实例(Consumer Instance ),它们共享一个公共的ID即 Group ID 。组内的所有消费者协调在一起消费订阅主题(Subscribed Topics)的所有分区(Partition)。当然一个分区只能有同一个消费者组的一个Consumer 实例消费。
Consumer Group 有三个特性:

  • Consumer Group 下可以有一个或多个Consumer 实例。 这里的实例可以是一个单独的进程,也可以是同一进程下的线程;
  • Group ID 是一个字符串, 在Kafka集群中唯一标识,Consumer Group
  • Consumer Group
    下所有实例订阅主体的单个分区,只能分配给组内某个Consumer实例消费。同一个分区消息可能被多个Group 消费。

二、Kafka消费者组解决了哪些问题?

传统的消息系统中,有两种消息引擎模型:点对点模型(消息队列)、发布/订阅模型
传统的两种消息系统各有优势,我们里对比一下:

  • 传统的消息队列模型的缺陷在于消息一旦被消费,就会从队列中删除,而且只能被下游的一个Consumer消费。严格的说这不是它的缺陷,
    这是它的一个特性。但很显然这种模型的伸缩性(Scalability)很差,因为下游的多个Consumer 都要“抢”
    这个共享消息队列的消息;
  • 发布/订阅模型,允许消息被多个Consumer 消费,但它的问题也是伸缩性不高,因为订阅者都必须订阅所有主体的所有分区。

Kafka 为规避传统消息两种模型的缺点,引入了 Consumer Group 机制:

  • Consumer Group 订阅多个主题后,组内的每个实例不要求一定要订阅主题的所有分区,它只会消费部分分区中的消息;
  • Consumer Group 之间彼此队里,互不影响,它们可以订阅同一组主题而互不干涉。加上Broker端的消息留存机制,KafkaConsumer Group 完美的避开了伸缩性差的问题;
  • kafka 是用Consumer Group机制,实现了,传统两大消息引擎。如果所有实例属于同一个Group,那么它实现的就是消息队列模型;如果所有实例分别属于不同的Group,且订阅了相同的主题,那么它就实现了发布/订阅模型;

三、Consumer Group 实例数量多少才合理?

最理想的情况是Consumer实例的数量应该等于该Group订阅主题的分区总数。例如:Consumer Group 订阅了 3个主题,分别是A、B、C,它们的分区数依次是1、2、3,那么通常情况下,为该Group 设置6Consumer实例是比较理想的情形。

阅读全文 »
0%