- 博客(139)
- 收藏
- 关注
原创 Golang 的协程调度小结
本文详细介绍了Golang调度器的演进过程和GPM模型设计。早期操作系统采用单进程/多线程模型存在切换成本高、资源竞争等问题。后来发展出用户态线程(协程)与内核态线程的N:1、1:1和M:N三种映射关系。Go语言采用M:N模型的Goroutine实现轻量级并发,通过GPM调度器(P表示处理器)解决早期全局队列锁竞争等问题。调度器采用线程复用、工作窃取和抢占式调度等策略,平衡了并发性能和资源利用率。Goroutine仅需几KB栈空间且可动态扩展,支持高并发场景。
2025-05-26 23:23:16
669
原创 Golang 访问 map 中的结构体字段时如何避免拷贝
在 Go 语言中,map 的值类型是按值传递的,这意味着访问 map 中的结构体会返回其副本,无法直接修改原始结构体。为了直接修改 map 中的结构体字段,可以采用以下两种方法: 使用指针:将 map 的值类型改为指向结构体的指针,这样通过键访问时返回的是指针,可以直接修改原始结构体字段。 显式修改并重新赋值:如果使用值类型,需要先取出结构体副本,修改后再将其重新赋值回 map。 通过这两种方式,可以避免值拷贝问题,直接修改 map 中的结构体字段...
2025-05-22 22:25:23
385
原创 Golang 内存模型小结
Go 的内存模型涉及内存分配、垃圾回收和并发访问的细节。内存分配分为堆内存和栈内存,堆内存由垃圾回收器管理,栈内存由操作系统管理。Go 通过逃逸分析决定对象分配在堆还是栈上。垃圾回收采用标记-清扫算法,具有增量式、并行和分代的特点,以减少停顿时间。Go 的并发访问共享变量时,需使用同步原语确保可见性和顺序性,避免因编译器或 CPU 重排序导致的并发错误。内存泄漏可能由未关闭资源、循环引用或持久化引用引起,需通过合理管理资源来避免...
2025-05-22 22:04:10
1327
原创 Golang 并发小结
并发编程中常见的问题包括数据竞争、死锁、活锁、协程泄漏、Channel 误用和调度抖动。数据竞争发生在多个协程同时读写共享变量时,死锁和活锁则涉及协程间的资源争夺和阻塞。协程泄漏和 Channel 误用可能导致资源耗尽或程序崩溃,而调度抖动则影响程序性能。为避免这些问题,建议使用同步机制如 sync.Mutex 或 sync.Map 来保护共享数据,使用 WaitGroup 或 context 管理协程生命周期,并确保 Channel 的正确操作。此外,使用协程池限制并发数,结合调试工具如 pprof...
2025-05-22 15:48:53
477
原创 浅谈 DNS 篡改劫持
DNS 篡改劫持是一种网络攻击或网络干扰行为,攻击者通过操控域名系统(DNS)的解析过程,拦截或篡改用户的 DNS 查询,从而将用户引导到恶意网站、钓鱼页面、广告页面,或阻止用户访问某些网站。DNS 篡改劫持可用于 DNS 域欺骗(攻击者通常目的是为了显示不需要的广告以产生收入)或用于网络钓鱼(为了让用户访问虚网站并窃取用户的数据和凭据)。互联网服务提供商(ISP)也可能通过 DNS 劫持,以接管用户的 DNS 请求,收集统计数据并在用户访问未知域名时返回广告或者屏蔽对特定网站的访问。
2024-08-12 16:24:40
940
原创 进程调度算法 & 内存页面置换算法 & 磁盘调度算法
磁盘的 I/O 操作通常是相对较慢的,其中寻道(即磁盘读取/写入头移动到目标位置的过程)是最耗时的部分之一。因此,磁盘调度算法的核心目标是尽可能减少寻道次数,以提高磁盘 I/O 的效率。
2024-07-23 11:36:28
605
原创 初识虚拟内存
虚拟内存(Virtual Memory)是一种计算机内存管理技术,它使得应用程序认为它们拥有连续的可用内存(一个大的地址空间),即使实际上可能被分散在物理内存和磁盘存储中。虚拟内存通过地址映射机制将程序使用的虚拟地址转换为物理地址,从而使得计算机系统可以更高效地利用内存资源。分段(Segmentation)优点逻辑单元管理:每个段可以表示一个逻辑单元(如代码段、数据段、堆栈段),有助于程序结构化灵活的大小:段的大小是可变的,可以根据实际需要进行分配,减少内存浪费。
2024-07-22 15:29:24
1723
原创 在 MySQL 某数据表中针对 username 字段建立唯一索引后,基于万级数据量和百万级数据量分别进行查询某用户 A,请问两次查询的性能耗时对比如何?
在 MySQL 某数据表中针对 username 字段建立唯一索引后,基于万级数据量和百万级数据量分别进行查询某用户 A,请问两次查询的性能耗时对比如何?
2024-04-02 17:43:53
944
原创 MySQL 深分页优化
而在第二条语句中,offset 设置为 6000000,则会在 InnoDB 的主键索引中获取第 0 ~ 6000000 + 10 条完整的行数据,返回给 server 层后根据 offset 的数值进行丢弃,只保留后面的 size 条数据,放到 server 层的结果集中,最后返回给客户端。针对第一条语句,server 层会调用 InnoDB 接口,在 InnoDB 的主键索引中获取到第 0 ~ 10 条完整的行数据,一次性返回给 server 层,并放到 server 层的结果集中,最后返回给客户端。
2023-09-20 23:40:05
489
原创 504 错误码排查
当出现 504 错误码时,表示请求超时,服务器无法及时响应请求,需要检查下应用是否有什么耗时的操作,比如是否出现了 SQL 慢查询、是否接口发生死循环、是否出现死锁等,同时需要关注服务器系统负载高不高。比如:本地开发时直连测试数据库并调用某个 API 接口的业务逻辑,同时开启 debug 模式,在更新某条数据时,假设此时他人也在操作更新相同的数据,就有可能会出现数据库死锁。因此在设计批量接口的时候,建议要限制传入的参数集合的大小,如果超过设置的最大参数集合的大小,则接口直接返回失败,并进行友好提示。
2023-09-20 17:24:53
946
原创 Kafka 问答小结
假设消费组内有 3 个消费者:C0、C1、C2,它们都订阅了 4 个主题:t0、t1、t2、t3,并且每个主题有 2 个分区,也就是说整个消费组订阅了:t0 p0、t0 p1、t1 p0、t1 p1、t2 p0、t2 p1、t3 p0、t3 p1 这 8 个分区。默认情况下,当消费者消费到消息后,就会自动提交位移。对于每个 Topic,该策略会将消费者组内所有订阅这个主题的消费者,按照名称的字典顺序排序,然后为每个消费者划分固定的区域,如果不够平均分配,那么字典排序靠前的就会多分配一个分区。
2023-09-19 17:59:28
307
转载 数据库与缓存的数据一致性
此外,「先更新数据库,再删除缓存」的方案,由于是两个操作,前面的所有分析都是建立在这两个操作都能同时执行成功的前提下。但是在两个更新请求并发执行的时候,会出现数据不一致的问题,因为更新数据库和更新缓存这两个操作是独立的,而程序又没有对操作做任何并发控制,那么当两个线程并发更新它们的话,就会因为写入顺序的不同,造成数据的不一致。所以,无论是「先更新数据库,再更新缓存」,还是「先更新缓存,再更新数据库」,这两个方案都存在并发问题,当两个请求并发更新同一条数据的时候,可能会出现缓存和数据库中的数据不一致的现象。
2023-09-04 11:32:13
149
原创 Redis 的主从复制、哨兵模式、集群脑裂
等网络恢复后,旧主节点会降级为从节点,再与新主节点进行同步复制时,由于从节点会清空自己的缓冲区,导致之前客户端写入的数据丢失。这时,哨兵也发现主节点失联,它就认为主节点挂了(但实际上主节点还是正常运行,只是网络出问题了),于是哨兵就会在「从节点」中选举出一个新主节点,这时集群就有两个主节点了 —— 脑裂出现了(相当于出现了两个大脑)。选举 leader 的过程其实也是一个投票的过程,在投票开始前,是哪个哨兵节点判断主节点为「客观下线」,这个哨兵节点就是候选者,所谓的候选者,就是想当 leader 的哨兵。
2023-08-25 19:03:07
889
原创 Redis 的混合持久化
加载完 RDB 的内容后,才会加载后半部分的 AOF 的内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主进程处理的操作命令,可以使得数据更少的丢失。那就将 RDB 和 AOF 混合使用,这个方法是在 Redis 4.0 提出的,即混合使用 AOF 日志和内存快照,也叫混合持久化。简单来说,使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。这样做的好处在于,重启 Redis 加载数据时,由于前半部分是 RDB 格式,加载的时候速度会很快。
2023-08-25 18:18:30
780
原创 Redis 执行 RDB 快照期间,主进程可以正常处理命令吗?
所以 Redis 在使用 bgsave 快照过程中,如果主进程修改了内存数据,不管是否是共享的内存数据,RDB 快照都无法写入主进程刚修改的数据,因为此时主进程的内存数据和子进程的内存数据已经分离了,子进程写入到 RDB 文件的内存数据只能是原本的内存数据(快照的定义)。如果主进程执行写操作,则被修改的数据会复制一份副本,然后 bgsave 子进程会把它的副本数据写入 RDB 文件,在这个过程中,主进程仍然可以直接修改原来的数据。注意,只有在发生修改内存数据的情况时,物理内存才会被复制一份。
2023-08-25 18:11:08
1329
2
原创 Redis 重写 AOF 日志期间,主进程可以正常处理命令吗?
触发重写机制后,主进程就会创建重写 AOF 的子进程,此时父子进程共享物理内存,重写子进程只会对这个内存进行只读,重写 AOF 子进程会读取数据库里的所有数据,并逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志(新的 AOF 文件)。简单来说,两者的虚拟空间不同,但其对应的物理空间是同一个。那么问题来了,重写 AOF 日志过程中,如果主进程修改了已经存在的 key-value,就会触发「写时复制」,此时这个 key-value 数据在子进程的内存数据就和在主进程的内存数据不一致了。
2023-08-25 17:59:12
1031
原创 进程中通信的方式有哪些?
Linux 内核提供了不少进程间通信的方式,其中最简单的方式就是管道,管道分为「匿名管道」和「命名管道」。假如 CPU 跑到 100%,你的解决思路是什么?
2023-08-25 10:59:09
122
原创 两阶段提交和三阶段提交的区别
两阶段提交协议包括预提交和提交两个阶段,而三阶段提交协议包括准备、提交和回滚三个阶段。例如,在两阶段提交协议中,如果协调者在预提交阶段发生故障,部分参与者已经提交了事务,而另一部分参与者由于无法与协调者通信而无法确认提交。两阶段提交和三阶段提交协议可以应用在主从数据库(Master-Slave Database)上,以实现在分布式环境下主从数据库之间的数据一致性。同样的,在三阶段提交协议中,尽管引入了准备阶段来解决部分阻塞等待问题,但仍然存在单点故障和网络故障等可能导致协议异常的情况。
2023-08-24 17:22:43
1487
原创 计算机网络 Q&A
DNS 解析过程浏览器缓存。当用户通过浏览器访问某域名时,浏览器首先会在自己的缓存中查找是否有该缓存对应的 IP 地址(曾经访问过该域名并且没有清空缓存)。系统缓存。当浏览器缓存中无域名对应的 IP 时,则会自动检测用户计算机系统 Hosts 文件中是否存在域名对应的 IP。路由器缓存。当浏览器和系统缓存中均无域名对应的 IP 时,则会进入路由器缓存中检查(上述三步均为客户端的 DNS 缓存 )。ISP(互联网服务提供商)DNS 缓存。当在用户客户端查找不到域名对应的 IP 地址时,则会进入 IS
2023-08-22 12:00:31
967
原创 Nginx 之按时间切割日志
// 其他配置if ($time_iso8601 ~"^(\d{4}-\d{2}-\d{2})") { set $time $1;}access_log xx/xx/access_$time.log main;// 其他配置
2023-08-22 10:29:05
272
2
原创 Docker 之 docker-compose.yml 配置项
指定Eggjs的容器运行环境,代替项目中的env配置文件// 其他配置web: // 其他配置 environment: -EGG_SERVER_ENV=prod // 此处指定了web容器的运行环境为prod, 等同于在Eggjs中env文件内设置成prod// 其他配置指定容器的网络,固定IP// 其他配置networks: network_name: ipam: config: -subnet: xx.xx.xx.xx/16容器间通信,当容器配置不在同一个
2023-08-22 10:28:41
325
原创 Nginx 配置之 proxy_set_header
参数proxy_set_header是用来设置请求头的,设置了请求头后,后端服务器就可以获取到这些变量值。server { listen 80; server_name 192.168.1.2; error_log /usr/local/etc/nginx/logs/test.error.log; access_log /usr/local/etc/nginx/logs/test.access.log; location /
2023-08-22 10:28:12
4284
原创 Eggjs 之 Sequelize 多数据源配置
config.${env}.jsconfig.sequelize = { datasources: [ // 默认的model目录 { // 加载所有的model到app.model & ctx.model delegate: 'model', // 要加载的model目录`app/model/*.js` baseDir: 'model', // 指明数据库类型 dialect: 'mysql', // 数据库配置 database: 'l
2023-08-22 10:27:46
410
原创 Node.js 内存溢出
最近部门的同学提了个新需求,希望协助查询出今年的一些系统数据并转成Excel表格的格式导给他们。接到新需求那就开干吧,首先咔咔写好了对应的前后端逻辑,测试服一跑,好家伙直接拉跨了,查看接口调用情况发现接口严重超时(前端在axios里全局设置了10s的超时时间),那就延长下timeout吧,一口气加到50s测试服的接口才算勉强能正常运行,但这肯定不行啊(代码提交上去肯定得被驳回的),那就只能想办法解决这个问题了。既然接口一口气处理不完,那第一反应就是加参数,大SQL拆成小SQL,设置查询处理范围一点一点.
2023-08-22 10:27:15
840
原创 Sequelize 配置
const _ = require('lodash');// 连接池pool { max: 20, min: 1, idle: 10000, acquire: 10000, evict: 60000, handleDisconnects: true,},// 钩子函数, 以驼峰命名法映射model字段和数据库字段hooks { beforeDefine: attributes => { Object.keys(attributes).forEach(key => {
2023-08-21 18:17:02
240
原创 Eggjs 集群环境下扩展定时任务类型 agent.js
// agent.js'use strict'const _func = async agent => { class ClusterStrategy extends agent.TimerScheduleStrategy { try { const scheduleName = this.key.replace(`${this.agent.baseDir}\\app\\schedule\\`, ''); const result = await agent.redis.set
2023-08-21 18:16:26
365
原创 Egg.js 获取真实 IP
realIp() { let realIp = this.get('X-FORWARDED-FOR'); realIp = realIp.split(',')[0]; if (!realIp || realIp == '127.0.0.1') { realIp = this.get('x-real-ip'); } if (!realIp) { realIp = this.request.ip; } return realIp;}
2023-08-21 18:15:41
507
1
原创 Node.js 之 Crypto 模块加解密
const crypto = require('crypto');// AES-256-ECB加密function aes(cryptoFn, body, key, encoding) { const cipher = crypto[cryptoFn]('aes-256-ecb', new Buffer(key), new Buffer('')); cipher.setAutoPadding(true); let ciph = cipher.update(body, encoding.input
2023-08-21 18:14:52
603
原创 Redis 分布式锁的实现方式
一般来说,在对数据进行“加锁”时,程序首先需要通过获取(acquire)锁来得到对数据进行排他性访问的能力,然后才能对数据执行一系列操作,最后还要将锁释放(release)给其他程序。对于能够被多个线程访问的共享内存数据结构(shared-memory data structure)来说,这种“先获取锁,然后执行操作,最后释放锁”的动作非常常见。Redis 使用 WATCH 命令来代替对数据进行加锁,因为 WATCH 只会在数据被其他客户端抢先修改了的情况下才通知执行了这个命令的客户端,而不会阻止其他客户
2023-08-21 18:12:05
309
原创 Golang 基础语法问答
map 的扩容:当 Go 的 map 长度增长到大于加载因子所需的 map 长度时,Go 语言就会产生一个新的 bucket 数组,然后把旧的 bucket 数组移到一个属性字段 oldbucket 中。静态类型声明的优势在于可以提供强类型检查。注意:并不是立刻把旧的数组中的元素转移到新的 bucket 中,而是只有当访问到具体的某个 bucket 的时候,才会把 bucket 中的数据转移到新的 bucket 中。通过明确指定变量的类型,可以对变量的用途有更好的描述,减少了对类型的猜测和理解上的困惑。
2023-08-18 00:07:41
908
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人