Golang的垃圾回收机制

GC 编程语言的内存管理方式就两种:自动和手动。像C/C++、Rust这种极致追求性能的语言就是要程序员手动进行内存空间的使用和释放。而像Java、Go等语言就是使用自动的方式进行内存管理,通过内存分配器就行内存分配,通过垃圾收集器进行垃圾回收。 GC,全称 Garbage Collection,即垃圾回收,就是一种自动内存管理的机制。通过垃圾回收程序员可以专注于写程序,不用考虑内存的释放问题。 常见的GC方式 主要包括两种: 引用计数GC

继续阅读

Golang的内存分配原理

程序运行中的堆和栈 一个程序在运行中占用的内存分为以下几部分: 栈stack:系统自动分配,存放函数的参数值,局部变量和方法调用,操作方法和数据结构中的栈类似,有栈容量,会出现溢栈现象。 堆heap:一般由程序员分配释放,程序员不释放就由OS释放(通过语言的GC),和数据结构中的堆不同,分配方式类似于链表。 全局区(静态区)static:全局变量和静态变量的存储,经过初始化的在一块区域,未初始化的在另一区域。 文字常量区:常量字符串存在这里。 程序代码区:函数体的二进制代码。 操作系统把磁盘上的可执行文件加载到内存之前,会把可执行文件中的代码,数据放在内存中合适的位置上,分配好堆栈,所有准备工作完成后程序才可以运行。内存布局如下所示:

继续阅读

MySQL的ORDER BY语句的工作方式

在日常开发应用时,经常会碰到根据某个字段排序后显示结果,那么 MySQL 底层对这种需要排序的查询到底是怎么处理的呢? 首先我们构造一张表: CREATE TABLE `t` ( `id` int(11) NOT NULL, `city` varchar(16) NOT NULL, `name` varchar(16) NOT NULL, `age` int(11) NOT NULL, `addr` varchar(128) DEFAULT NULL, PRIMARY KEY (`id`), KEY `city` (`city`) ) ENGINE=InnoDB; 这个表有 id、city、name、age等几个字段,主键是 id,city 字段创建了普通索引。

继续阅读

Goroutine的调度模型

Goroutine Goroutine是一个与其他 goroutines 并行运行在同一地址空间的Go函数或者方法,一个运行的Go程序由一个或多个goroutines组成。 Go程序在执行时实际上是通过Go的runtime与操作系统内核进行交互。Go程序中创建的goroutines、内存对象等都是交由runtime去管理的,如下图: Goroutine和Thread的区别 内存占用不同 创建一个goroutine的栈的大小,在 go1.4 版本以后是 2KB,运行过程中如果栈不够再进行扩容(这部分内容可以查看Go的内存管理这篇文章)。

继续阅读

性能优化的分析思路

系统性能的定义 Throughput:吞吐量 Latency:请求耗时 Error rate:错误率

继续阅读

Raft算法介绍

Raft算法介绍 在分布式系统中,为了消除单点故障提高系统可用性,通常会使用副本来进行容错,但这会带来另一个问题,即如何保证多个副本之间的一致性。 强一致性 强一致性不是说所有节点在任意时刻的状态都是一致的,而是指让一个分布式系统看起来好像是个单机一样,所有操作都是原子的,不管什么故障产生,都不会让分布式系统的节点间的数据出现不一致,让应用层可以忽略底层分布式系统之间数据的同步问题,像操作一个单机一样操作这个分布式系统。 共识算法 共识算法就是用来实现强一致性的算法,可以保证在小于一半的节点发生故障时,整个系统依然是正常可用的。 共识算法是强一致性分布式系统的基石,比较有名的就是Zookeeper所使用的Paxos算法,Raft也是共识算法的一种,它是Paxos算法的一个简化版本,更易于理解和实现,被很多的分布式系统使用,比较有名的就是Etcd、Consul,TiKV等,MySQL集群中也使用到了raft算法。 Raft算法的三个核心问题 对于一个基于raft算法的分布式集群来说,当我们通过客户端向集群发起一些读写操作时,首先需要确定的是需要有一个节点对操作进行处理和响应,这个节点被称为是主节点(Leader),其他节点就叫做从节点(Follower)。Raft算法的第一个核心问题就是选主,需要确定集群中哪个节点作为主节点。 确定了主节点后,主节点就要接收到了客户端的请求,比如接到一个写入数据的操作后,不仅需要将数据保存在本地,还要将数据分发给集群中的其他从属节点。Raft算法需要在保证集群中的大多数节点都接收到了这次操作并且完成同步之后,主节点才可以给客户端操作成功的响应。这个操作分发的过程就是日志复制,即raft算法中主节点是将操作包装成日志的形式发送给所有从节点的。

继续阅读

防缓存击穿利器singleflight介绍

缓存击穿 一般一个并发量大的服务,往往会在服务和数据库之间增加一层缓存,来减轻数据库的压力,因为数据库在高并发时往往会是整个系统的瓶颈。大概的过程就是查询时,先从缓存中查询,缓存中查不到,则从数据库中查询,并把数据回填到缓存中,再返回给用户,那么下次用户访问同一份数据时,就可以命中缓存而不用访问数据库,以此减轻数据库的压力。而在系统刚启动时一般也会采用缓存预热的方式,把数据库中的一些可能的热点数据预先加载到缓存中,以防止系统刚刚启动时数据库的压力过大。 而缓存中的key往往都需要有过期时间,当缓存中(Redis或者Memcached)的某个热点key,在承受大量并发时突然过期,导致查询这个key的所有请求穿透缓存到达数据库,增大数据库的压力,严重时甚至可能让数据库宕机,这就是缓存击穿。就像下图这样,所有请求都透传到了数据库: 解决这个问题比较重型的武器是分布式锁,即缓存miss后访问数据库需要先获取分布式锁,拿到分布式锁后才能从数据库中查到想要的数据,没拿到分布式锁就需要等待其他占有锁的线程完成从数据库中取数据和缓存的回填操作后,再从缓存中获取数据并返回。 但是这种解决方式消耗资源过大,且并且不是每个系统都需要分布式锁。singleflight就是一个解决该问题的利器。 Singleflight的使用 singleflight 库,直译过来就是单飞,这个库的主要作用就是将一组相同的请求合并成一个请求,实际上只会去请求一次,然后对所有的请求返回相同的结果即可。当并发访问数据库中某个数据时,可以通过singleflight保证在一定的时间周期内,同一个key的并发访问实际上只有一次请求真正访问数据库,以此来大大降低数据库的压力,如下图: 使用方式 首先获取这个包

继续阅读

微服务知识点总结

微服务的演进过程 单体应用 单体应用时代有两个主流流派: LAMP架构:Linux系统 + Apache/Nginx服务器 + MySQL数据库 + 一种脚本语言,比如PHP,Python,Ruby,Perl等。特点是轻量级,价格低,跨平台,并且十分易于开发。 MVC架构:Model-View-Controller,例如Spring + IBatis/Hibernate + Tomcat。它有较低的耦合性,可重用性高,且部署快速的特点。 单体应用架构的优点:

继续阅读

CPU的cache line和false sharding问题

CPU的多级缓存 随着计算机进入多核时代,CPU处理速度和内存之间的鸿沟越来越大,因此多核CPU都通过缓存行提高CPU和内存的交互效率。CPU 缓存(Cache Memory)是位于 CPU 与内存之间的临时存储器,它的容量比内存小的多但是交换速度却比内存要快得多。在缓存中的数据是内存中的一小部分,但这一小部分是短时间内 CPU 即将访问的,当 CPU 调用大量数据时,就可避开内存直接从缓存中调用,从而加快读取速度。

继续阅读

Golang的mutex底层实现

互斥锁 互斥锁的数据结构 互斥锁是并发程序中对一个共享资源进行访问控制的常用手段,Golang中提供了mutex作为互斥锁的实现。mutex的数据结构如下: // 定义在src/sync/mutex.go type Mutex struct{ state int32 sema uint32 } 包括两个字段:

继续阅读