Golang

go协程工具WaitGroup

go协程工具WaitGroup waitGroup 是go语言非常常用的一个协程的流程工具,可以在指定位置阻塞等待指定数量的协程执行完成,从字面意思理解即是阻塞等待指定组的协程执行完成,在需要控制或者等待一批协程执行完成时可以使用waitGroup 实现该效果,其实该包的原理也是使用了channel实现的协程同步机制,通过在指定位置添加waitGroup.Wait() 方法来阻塞等待指定数量的协程执行完成,在协程执行完成后调用waitGroup.Done() 方法来通知waitGroup 协程执行完成,waitGroup 内部维护了一个计数器,当计数器为0时,waitGroup.Wait() 方法才会返回,否则会阻塞等待。 type WaitGroup struct { } func (wg WaitGroup) Add(delta int) func (wg WaitGroup) Done() func (wg WaitGroup) Wait() Add(delta int) 每次激活想要被等待完成的协程之前,先调用Add(),用来设置或添加要等待完成的goroutine数量 Done() 表示该协程执行完成,执行Done()会将waitGroup 内部的计数器减1 Wait() 表示等待所有的协程执行完成,当waitGroup 内部的计数器为0时,Wait() 方法才会返回,否则会阻塞等待 一般在所有的协程执行完成后调用Wait() 方法,等待所有的协程执行完成 func (wg WaitGroup) Add(delta int) { // 先从 state 当中把数据和信号量取出来 statep, semap := wg.state() // 在 […]

Golang

Go语言syncMap实现的功能和使用场景

Go语言syncMap实现的功能和使用场景 在go语言提供的标准map类型中,如果涉及到协程读取map的场景,会存在并发读写的问题,并发操作map的会导致数据不一致, 为什么map不是并发安全的? 因为map的读写操作不是原子操作,当多个协程同时读写map时,会发生脏读和脏写导致数据不一致的问题。 在go 语言1.9版本后引入了syncMap,可以提供更加便捷的并发map操作方法, 例如: var m sync.Map 可以使用syncMap的方法来进行并发安全的map操作,例如: m.Store(key, value) m.Load(key) m.Delete(key) 这些方法都是并发安全的,不会导致数据不一致的问题。 但是该数据类型仅适合一定程度上的读多写少场景,syncMap内部使用了读写锁来实现并发安全,当写操作比较多时,会导致读操作的阻塞,影响性能。 源码:https://github.com/golang/go/blob/go1.23.0/src/sync/map.go func (m *Map) Swap(key, value any) (previous any, loaded bool) { read := m.loadReadOnly() if e, ok := read.m[key]; ok { if v, ok := e.trySwap(&value); ok { if v == nil { return nil, false }

Redis

redis-Sub/Pub消息订阅

redis-Sub/Pub消息订阅 Sub/Pub 消息订阅,是redis 中一个处理消息通知和数据一致的机制,它可以实现实时消息系统,比如普通的即时聊天,群聊等功能。 sub/pub 消息订阅案例demo package main import ( “fmt” “time” “github.com/go-redis/redis” ) func main() { client := redis.NewClient(&redis.Options{ Addr: “localhost:6379”, Password: “”, // no password set DB: 0, // use default DB }) pubsub := client.Subscribe(“chat”) go func() { for msg := range pubsub.Channel() { fmt.Println(msg) } }() time.Sleep(time.Second) } 使用场景 独立进程数据交互: redis是基于内存实现的,执行和计算流转效率速度非常快,如有两个独立进程需要进行数据交互和通知时,subpub是一个非常好的数据通知交互的方式,比传统的http请求和websocket请求实现的网络版数据交互方式时间效率要高很多

Uncategorized

Mysql的索引是如何实现数据的快速查找的

Mysql的索引是如何实现数据的快速查找的 inndb的存储引擎,索引实现的数据结构是B+树的一个数据结构,B+树是一个比较矮胖的数据结构,本身的特性比较适合在io磁盘的场景下做数据索引,可以减少大量不必要的io查询操作 B+树的数据结构本身就是一个平衡树,每个节点的子节点数量是固定的,每个节点的子节点数量是相等的 B+树的每个节点都有一个指向下一个节点的指针,这个指针可以用来遍历整个树 B+树的每个叶子节点都有一个指向下一个叶子节点的指针,这个指针可以用来遍历整个树

Uncategorized

Mysql-sql实例分析

Mysql-sql实例分析 Select A,B,C FROM t WHERE D=? AND E=?; 索引:D,E 组合索引 Mysql 执行这个sql 查询数据,会先根据索引D 查找符合条件的叶子节点,然后根据索引E 查找符合条件的叶子节点,最后返回符合条件的结果 问题:索引不变,新增一个字段F,F字段没有索引,sql也不变的场景下,查询效率会不会有变化? 我认为这个场景不会有变化,sql语句使用同样的索引命中同样的叶子结点,然后查询B+树的主树,根据id去检索数据,查询命中数据条目,最后将用户需要的数据字段检出返回给用户

Uncategorized

为什么要从php迁移到go

为什么要从php迁移到go 今天约到一个公司面试,聊到他们公司正在从php语言迁移到go语言,我就想知道他们为什么要迁移到go语言,有什么优势吗? 他们的回答是: 1. go语言是一种编译型语言,编译后直接运行在操作系统上,没有解释器的 overhead,所以性能要比 php 高很多。 2. go语言天生支持并发,通过 goroutine 和 channel 可以很方便地实现并发编程,而 php 原生要实现并发编程比较繁琐。 for example: 在实际使用场景下: php在需要进行大量数据计算和数据清洗的场景下,原生php只能顺序执行,在遇到计算体量比较大的场景下,如果顺序执行的时间成本比较高,比如要计算10个模块的子数据块的金额数据,一个数据块需要500ms 10个数据块则是5000ms go在进行大量数据计算和数据清洗的场景下,通过 goroutine 和 channel 可以很方便地实现并发编程,每个数据块可以分配一个 goroutine 来并行计算,最后通过 channel 来合并结果,这样可以大大提高计算效率,减少时间成本。同样的计算场景下10个子模块的数据可以将10个子模块的数据计算叠加到一起进行并发计算,计算需要的时间则是10个子模块数据计算中计算时间最长的单元的时间成本,相比于10个模块时间成本的和计算的时间成本是指数级的提高。 3. go语言的学习成本和php的swoole型框架的学习成本比较,底层知识的学习成本是一样,相近的,在相同学习成本的场景下,go语言普适性更高,性能更好,社区支持更长久,第三方框架和语言功能适配性会更好 4. go语言生成的二进制包能更好的迁移和服务器扩容

架构设计

本地消息表简介

本地消息表简介 本地消息表是分布式事务的一种实现方式,解决了分布式场景下数据最终一致性问题,是分布式事务相对简单可靠的解决方案,有一定的数据延迟性 实现思路 例:如在支付场景中,当用户支付完订单后, 订单服务会向本地消息表插入一条消息,消息内容为订单支付成功 订单服务会向支付服务发送一条消息,消息内容为订单支付成功 订单支付的业务逻辑和本地消息表的写入,生成订单相关业务逻辑和数据,订单的异步MQ任务投递是在同一个事务中,其属性是原子的 订单的异步MQ任务会被需要的服务消费,消费后的结果会更新本地消息表的状态 本地消息表会定时扫描,将状态为未处理的消息重新投递到MQ中 其他服务在需要查询订单状态时,会先查询本地消息表,若状态为已处理,则直接返回结果,否则向订单服务查询 并且需要一个定时扫描程序定期扫描本地消息表,检查检索没有正确投递,或者下游服务没有正确应答的消息,将其重新投递到MQ中 需要注意的是重新投递的MQ任务也可能会投递、消费失败,则需要在本地消息表中记录重试次数,超过最大重试次数则需要人工处理,注意在所有下游服务拉去MQ时的消息记录和日志需要完整,并且记录下每次消费的结果(正确或异常),方便后续分析和处理 问题 MQ服务,和其他服务在交互环节中会有数据不一致,延迟,消息丢失,服务挂机的情况发生,有服务宕机,消息丢失,进程消费异常等情况是会经常发生出现的,如何解决该问题 需要通过生成全局消息表traceId的方式完善该问题的逻辑,全局消息表是全局唯一,并且幂等,如果有消息丢失进程挂起,假死等情况需要将全局的本地消息表记录中的mq数据重复投递, 在其他分布式服务中需要幂等该进程的traceId的幂等消费,多次投递多次消费的结果是一致的 本地消息表是和业务逻辑相对耦合的,如果业务逻辑发生变化,需要同步修改本地消息表的处理逻辑,否则会导致数据不一致的问题,所以该实现方式对业务侵入性较强,不适合处理通用性较强的场景,所以使用时需要注意代码的后期维护几率和成本,如果后期维护成本较高,并且业务逻辑频繁变更,建议使用其他分布式事务实现方式 https://www.ierchina.com

架构设计

访问微信公众号的网址非常慢(分析与优化方案)

访问微信公众号的网址非常慢(分析与优化方案) 问题场景 从公司站点或落地页跳转到微信公众号文章链接(如 mp.weixin.qq.com)时,整体打开耗时明显偏高 可能伴随多次重定向、接口串行校验、图片与第三方脚本阻塞加载等现象 关键现象与指标 DNS 解析耗时高、TLS 握手时间长、TTFB 偏高 重定向次数多(>1 次常见会显著拉长总耗时) 首屏阻塞资源过多(JS 同步加载、未压缩的图片、第三方埋点) 后端接口响应慢(数据库查询无索引、N+1、分页不当) 根因分析维度 链路与不可控边界 微信域名与其网络出口不可控,跨运营商/跨地区链路质量差异大 ICMP 常被屏蔽,Ping 不一定可用,需用 HTTP 侧指标判定 跳转逻辑与认证 过多的 302/301 重定向链、签名校验串行执行、未缓存的配置接口 跳转页存在动态渲染与接口依赖,导致首屏耗时增加 前端资源与页面结构 大体积图片、同步第三方脚本、阻塞式 CSS/JS、未启用缓存与压缩 服务端与网关 未开启 HTTP/2、KeepAlive 配置不合理、TLS 会话复用与 OCSP Stapling 缺失 数据库与存储 查询缺索引、Scan 过重、N+1 查询、分页深页、大量无用字段加载 诊断与定位步骤 浏览器侧 Chrome DevTools 网络面板:查看 DNS、TLS、TTFB、重定向链、阻塞资源瀑布图 Performance/Coverage:识别长任务与未使用代码 Lighthouse/WebPageTest:获取实验室指标与真实网络行为 命令行与网络 使用 curl 统计时延:curl

架构设计

在网页中 HTTP 访问一个网址发生了什么?

在网页中 HTTP 访问一个网址发生了什么? 简要流程 解析 URL 与安全预处理 DNS 解析(目标域名 IP) 建立连接(TCP/80 或 TCP/443;或 QUIC/UDP) TLS 握手(若为 HTTPS) 浏览器发起 HTTP 请求 服务器处理请求(反向代理/应用/数据库/缓存) 返回 HTTP 响应(状态码、响应头与内容) 浏览器解析与渲染页面 缓存与后续优化(前端/CDN/代理) 连接复用或关闭 环节 1. 地址栏输入与预处理 自动补全与安全检查:浏览器可能进行历史记录/书签匹配、恶意站点拦截、HTTPS 先行策略(HSTS)。 本地命中与拦截:Service Worker 有权拦截同源请求,命中离线缓存或走自定义网络策略。 2. 域名解析(DNS) 解析顺序:hosts 文件 浏览器/系统 DNS 缓存 本地域名解析器 递归/权威 DNS。 关键记录:A/AAAA(IPv4/IPv6)、CNAME(别名)。CDN 会返回最优边缘节点 IP(ECS/GeoDNS)。 安全与隐私:DoH/DoT(HTTPS/TLS 下的 DNS)、DNSSEC 验证。解析结果受 TTL 控制被缓存。 3.

Uncategorized

MySQL事务隔离级别

MySQL事务隔离级别 事务四大特性(ACID) 特性 说明 MySQL 实现方式 原子性 (Atomicity) 事务要么全部完成,要么全部不完成 Undo Log(回滚日志) 一致性 (Consistency) 事务前后数据完整性一致 业务逻辑 + 数据库约束 隔离性 (Isolation) 事务之间互不干扰 MVCC + 锁机制 持久性 (Durability) 事务提交后永久生效 Redo Log(重做日志) 四种隔离级别 1. READ UNCOMMITTED(读未提交) SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 特点: 可以读取其他事务未提交的数据 脏读、不可重复读、幻读 都可能发生 使用场景: 对数据一致性要求极低的场景 统计类查询(如 count(*),允许一定的数据偏差) 实际生产环境几乎不使用 示例: — 事务A BEGIN; UPDATE accounts SET

Scroll to Top