Go sheduler 是什么?

Go sheduler是什么? Go 程序的执行有两个层面:Go Program 和 Runtime,即用户程序和运行时。它们之间通过函数调用来实现内存管理、channel 通信、goroutine 创建等功能。用户程序进行的系统调用都会被 Runtime 拦截,以此来帮助它进行调度以及垃圾回收相关的工作。 Go scheduler 可以说是 Go 运行时的一个最重要的 部分了。 Runtime 维护所有的 goroutine ,并通过 scheduler 来进行调度。goroutine 和 threads 是独立的, 但是 goroutine 要依赖 threads 才能执行。 Go 程序执行的高效和 scheduler 的调度是分不开的。 实际上在操作系统看来,所有的程序都是在执行多线程。将 goroutine 调度到线程上执行,仅仅是 runtime 层面的一个概念,在操作系统之上的层面,操作系统并不能感知到 goroutine 的存在。 G、M、P三个基础的结构体来实现 goroutine 的调度: G 代表一个 goroutine,它包含:表示 goroutine 栈的一些字段,指示当前 goroutine 的状态,指示当前运行到的指令地址,也就是 PC 值。 M 表示内核线程,包含正在运行的 goroutine 等字段。 P 代表一个虚拟的CPU Processor,它维护一个处于 Runnable 状态的 goroutine 队列,M 需要获得 P 才能运行 G。 当然还有一个核心的结构体:sched,它总揽全局,维持整个调度器的运行。 Runtime 起始时会启动一些 G:垃圾回收的 G,执行调度的 G,运行用户代码的 G;并且会创建一 个 M 用来开始 G 的运行。随着时间的推移,G和M的创建数量逐渐增多。 在 Go 的早期版本,并没有 P 这个结构体,M 必须从一个全局的队列里获取要运行的 G,因此需要获取一个全局的锁,当并发量大的时候,锁就成了瓶颈。后来调度器在 Dmitry Vyukov (Go 语言运行时 runtime 和调度器的核心贡献者之一) ,加 上了 P 结构体。每个 P 维护一个处于 Runnable 状态的 G 的队列,解决了原来的全局锁问题。 ...

2025年9月7日 · Mumu

内存对齐(memory alignment)

内存对齐(Memory Alignment)是计算机系统架构和编程中的一个基本概念,它指的是数据在内存中的存储地址必须是某个值的整数倍。这个“某个值”通常是该数据类型的大小或其最大成员的大小(在结构体中)。 通过分析以下具体实例加深内存对齐理解。 type itab struct { inter *interfacetype // 接口类型信息 _type *_type // 实现接口的具体类型信息 hash uint32 // 类型 hash 值 _ [4]byte fun [1]uintptr // 实现接口方法的函数地址 } 一、Go 的结构体内存布局规则 Go 里每个字段在内存中都有一个偏移量(offset),而编译器会自动插入 padding(填充字节),以保证每个字段都按其类型对齐(alignment)。 规则大致是: 每个字段的起始地址必须是该字段类型的对齐倍数。 比如:uint32 对齐要求 4 字节,uintptr(在 64 位机上)对齐要求 8 字节。 整个结构体的大小必须是其内部最大对齐单位的整数倍。 编译器自动插入 padding 字节,但有时源码里会显式加 _ [N]byte 来占位或兼容 ABI。 二、itab 的字段分析(以 64位架构为例) 我们来计算每个字段的内存偏移: 字段 类型 大小 (Size) 自身对齐值 (Align) 偏移量 (Offset)单位:字节 备注 inter *interfacetype 8 字节 8 字节 0 → 8 8 字节对齐 _type *_type 8 字节 8 字节 8→16 8 是 8 的倍数,已对齐 hash uint32 4 字节 4 字节 16→20 16 是 4 的倍数,已对齐 _ [4]byte 4 字节 1 字节 20→24 4 字节的填充 (Padding) fun [1]uintptr 8 字节 8 字节 24→32 8 字节对齐 24 是 8 的倍数 总大小32 字节,结构体最大对齐是 8 字节,总大小 32 是 8 的倍数。 ...

2025年9月5日 · Mumu

主键和唯一键?区别是什么?

主键(Primary Key)是唯一标识每行的非空字段,每表只能有一个; 唯一键(Unique Key)是保证字段值唯一,但允许为NULL,每表可有多个。 主键 (Primary Key) 定义:用于唯一标识表中每一行数据的字段或字段组合,是表的核心标识。 作用:为其他表提供关联引用的核心字段,唯一标识表中的每一行数据。 特点 唯一性:所有主键值必须唯一,不可重复。 非空性:主键字段禁止为 NULL。 单例性:每张表仅能定义一个主键(但可以是多字段的联合主键)。 索引支持:数据库一般会为主键自动创建索引来提高查询效率。 唯一键 (Unique Key) 定义:用于确保字段或字段组合的值唯一,但非表的唯一标识。 作用:提供辅助的唯一性约束,便于业务逻辑使用。确保指定列或列组合在表中的数据唯一,防止重复数据产生。 特点 唯一性:字段值不可重复,但允许 NULL,具体可允许多少个 NULL,取决于数据库实现,如 MySQL 可以有多个。 多例性:一张表可定义多个唯一键,用来约束不同的业务属性。 索引支持:数据库也通常会为唯一键创建索引,提高检索速度。 主要区别 数量限制 主键:每张表只能有一个。 唯一键:一张表可以定义多个。 用途侧重 主键:这一列(或列组合)在逻辑上就是记录的“身份证”,最常用来建立实体与实体间的关联。 唯一键:更多是防止重复数据,保证业务字段(如邮箱、手机号等)具有唯一性,但并非必然用作数据行的主标识。 约束规则 主键:强制非空(NOT NULL),插入数据时必须显式指定值,但若设置 AUTO_INCREMENT,MySQL 会自动分配下一个可用值。插入后通常不推荐更新主键值。 唯一键:允许 NULL 值,例如:MySQL允许多个 NULL视为不冲突而SQL Server仅允许一个 NULL。值可更新,但需保证新值唯一。 索引与性能 主键:默认创建聚集索引(如 MySQL InnoDB 、SQL Server),物理上按主键顺序存储数据,范围查询非常高效。 唯一键:默认创建非聚集索引,逻辑上维护唯一性,适合等值查询。 简单示例 主键场景:在“用户表”中,UserID 作为主键,保证每位用户都能被独一无二地识别和引用。 唯一键场景:在同一个用户表中,Email 字段也要求不能重复时,就可以为 Email 设置唯一键;此时允许它为 NULL,但实际业务上通常会要求非 NULL 并且唯一。 总结 主键和唯一键都用于保证数据唯一性,但主键更侧重于表的标识和引用,而唯一键更侧重于业务属性的唯一性约束。在实际应用中,通常会结合使用两者来实现数据完整性和业务规则的约束。 主键其实就是用来标识每行数据身份的核心字段或字段组合,必须保证主键值的非空性和唯一性。非空性是指插入主键值数据时必须显式指定值,但如果设置了自增(AUTO_INCREMENT),插入数据时就不用手动指定值,数据库会自动分配下一个可用的数值,另外通常不建议在插入后再去修改主键。唯一性是指主键值数据整能够唯一标识表中的一行,此外数据库一般会自动给它创建聚簇索引。 相比之下,唯一键虽然也要保证相应字段的值的唯一性,但它更偏向业务层面的唯一性控制,不一定要当成每行数据的主识别字段。它允许为空,而且在不同的数据库里,对空值的处理也不一样,有的可以插入多个空值,例如MySQL。有的只能插一次,例如SQL Server 。一个表中可以同时存在多个唯一键,每个唯一键都会有对应的非聚簇索引来提升检索效率,这样就能在业务里确保不同属性都能做到唯一。 两者主要区别,首先是每张表只能有一个主键,但可以有多个唯一键。 其次是主键一定不能为空,而唯一键通常允许空值。 还有,主键往往是用来跟别的表建立关联,比如用户表中的 UserID 作为主键,就能让其他表引用这个字段来关联用户信息。唯一键更多是防止个别业务字段的重复,比如用户表中的 Email 地址如果也要求唯一,可以设置成唯一键,这样就能保证任何两个用户都不会用到相同的邮箱。 最后,数据库在主键和唯一键上也有不同的索引方式,InnoDB 或 SQL Server 的主键会采用聚集索引,让物理存储和主键顺序相关联,范围查询时会更高效;而唯一键通常是非聚集索引,比较侧重等值查询。 总之,主键和唯一键都用于保证数据唯一性,但主键更侧重于表的标识和引用,而唯一键更侧重于业务属性的唯一性约束。在实际应用中,通常会结合使用两者来实现数据完整性和业务规则的约束。

2025年9月4日 · Mumu

SQL_Index 索引

在 Go 后端开发中,我们通常使用 MySQL 或 PostgreSQL 等关系型数据库,索引设计的好坏直接决定了服务接口的响应速度。 1.索引设计 1.1 选择合适的列作为索引: 选择性高(High Selectivity): 索引列的不重复值越多越好。例如,用户 ID(唯一)比性别(只有两三种值)更适合作为索引。 场景: 在设计用户服务时,user_id、email 等是理想的索引列。 常用作查询条件(Where): 经常出现在 WHERE 子句中的列,或者用于连接(JOIN)的列。 排序/分组(Order By/Group By): 经常用于排序或分组的列。 1.2 考虑联合索引(Composite Index): 最左前缀原则 (Leftmost Prefix Principle): 这是联合索引设计的核心。如果创建了 (A, B, C) 的联合索引,它可以用于查询 WHERE A = ?、WHERE A = ? AND B = ?、WHERE A = ? AND B = ? AND C = ?,但不能单独用于 WHERE B = ? 或 WHERE C = ?。 场景: 在设计订单查询接口时,如果经常查询 WHERE user_id = ? AND order_status = ?,应建立 (user_id, order_status) 的联合索引。 1.3 覆盖索引 (Covering Index): 如果查询的所有字段都包含在索引中,那么数据库不需要回表(查找主键对应的数据行),直接从索引中返回数据即可。这能大幅提升性能。 场景: 当你需要查询某个用户的订单状态和创建时间,只创建 (user_id, status, create_time) 的联合索引。查询语句为 SELECT status, create_time FROM orders WHERE user_id = ?,数据库直接通过索引就能拿到结果。 1.4 索引数量的平衡: 索引不是越多越好。每个索引都会占用磁盘空间,并且在进行 INSERT, UPDATE, DELETE 操作时,数据库需要维护索引,造成写操作性能下降。 场景: 在设计高写入量的日志表或消息表时,应尽量少建索引,只保留用于最核心查询的索引。 2.高效命中策略 使用 Explain : 在 Go 后端进行复杂查询优化时,一定要在测试环境使用 EXPLAIN 命令分析 SQL 语句,确保 type 列不是 ALL(全表扫描),key 列使用了正确的索引。 ...

2025年9月3日 · Mumu

slice 和 数组

Go 语言里 slice 和 map 是非常有用的两个内置数据结构, 线上的工程代码几乎不可能绕开它们。 1.数组与切片 因为切片(slice)比数组更好用,也更安全,Go 推荐使用 slice 而不是数组。本节内容比较 了 slice 和数组的区别,也研究了 slice 的一些特有的性质。 1.1数组和切片有何异同 Go 语言中的切片(slice)结构的本质是对数组的封装,它描述一个数组的片段。无论是数组 还是切片,都可以通过下标来访问单个元素。 数组是定长的,长度定义好之后,不能再更改。在 Go 语言中,数组是不常见的,因为其长度 是类型的一部分,限制了它的表达能力,比如 [3]int 和 [4]int 就是不同的类型。而切片则非常灵 活,它可以动态地扩容,且切片的类型和长度无关。 func main() { arr1 := [1]int{1} arr2 := [2]int{1, 2} if arr1 == arr2 { fmt.Println("equal type") } } 尝试运行,报编译错误: ./test.go:16:10: invalid operation: arr1 == arr2 (mismatched types [1]int and [2]int) 因为两个数组的长度不同,根本就不是同一类型,因此不能进行比较。 数组是一片连续的内存,切片实际上是一个结构体,包含三个字段:长度、容量、底层数组。 // src/runtime/slice.go type slice struct { array unsafe.Pointer // 元素指针 len int // 长度 cap int // 容量 } 注意,底层数组可以被多个切片同时指向,因此对一个切 片的元素进行操作有可能会影响到其他切片。 ...

2025年9月2日 · Mumu

我的第一篇博客

今天开始记录我在 Go 高并发项目中的一些心得体会。

2025年9月2日 · Mumu

First Encounter with Hugo

🚀 什么是 Hugo? Hugo 是一个基于 Go 语言编写的 静态网站生成器。 它的最大特点是——速度极快、部署方便、几乎零依赖。 使用 Hugo,你可以用 Markdown 写文章,然后自动生成一个完整的博客网站。 🛠️ 安装 Hugo 在 macOS 上: brew install hugo

2025年9月1日 · Beeta

我的项目

电商购物平台 & 管理后台(Go + Iris + MySQL + Redis + RabbitMQ + Nginx) 前后端一体化 Web 项目,从前端 UI、后端接口、数据库设计到上线部署。 这是我从零开始独立完成的完整的前后端一体化Web项目,从前端UI、后端接口、数据库设计到上线部署,都是自己亲手实现。 项目的目标是构建一个完整的电商流程:商品浏览、分类展示、购物车、订单流程,以及后台的商品管理、订单管理、用户管理等。 用户端界面展示 “宇宙购物”(SPACE SHOPPING)的电商平台,整体风格现代、简洁、实用。 瀑布流布局 & 搜索栏 商品分类导航(多级导航栏) 商品详情(加入购物车、立即抢购) 高性能排行榜单 用户下单(确认和生成订单) 页面的 UI 风格参考了大型电商平台(如淘宝、京东)的布局结构,整体结构清晰,视觉优雅。 管理后台系统 后台系统提供api管理全站信息,是自己设计的简洁的CMS管理平台。 订单管理 商品管理 支持新增、编辑、删除商品,上传图片,设置库存、分类等。 商品分类管理 查看商品分类明细和支持新增、编辑、删除等操作 用户管理 查看用户列表、状态,支持冻结/启用。 管理员管理 查看管理员列表、状态,支持冻结/启用。 技术栈 前端: HTML + CSS + JS 部分页面使用 Vue 简化动态逻辑 管理后台基于 Admin 模板自定义开发 后端: Go(Golang) Iris Web 框架 MySQL(核心数据) + Redis(缓存 & session) JWT 登录认证 RabbitMQ(异步消息) Nginx(反向代理) 部署: Ubuntu Server systemd 服务托管(自动重启、日志收集) Nginx 反代 / 静态资源服务 二进制打包发布,软链接切换版本(current → eshop_v2) 项目亮点 支持高并发的基础架构搭建 项目内集成了: Redis 缓存 RabbitMQ 消息队列 Nginx 负载均衡预留接口 为将来扩展秒杀、异步通知等功能做了铺垫。 碎碎念 哈哈当然啦,在开发的整个过程还是有许多收获的。 例如加深对Go语言的熟悉程度、各种中间件和相互之间的如何应用、如何构建一个高可靠和高可用的系统、 在高并发条件下如何容错、如何解决数据可能丢失和学会排查系统错误、端口冲突、模板路径问题等等。 也许在不久还会有更多出色的应用,敬请期待吧~

2024年12月31日 · Mumu