自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

月守护的博客

记录过程,分享知识,学习经验,感悟人生

  • 博客(169)
  • 收藏
  • 关注

原创 使用go pprof进行golang程序内存分析

use pprof to fix oom

2022-11-18 11:58:47 2402 1

原创 乐观锁,悲观锁与Golang

悲观锁悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。乐观锁乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适

2021-05-07 16:49:38 1677

原创 CPU Cache体系对Go程序的影响

引言看一段测试代码:package mainimport "testing"func createMatrix(size int) [][]int64 { matrix := make([][]int64, size) for i := 0; i < size; i++ { matrix[i] = make([]int64, size) } return matrix}const matrixLength = 6400func BenchmarkMatrixCombi

2021-04-22 17:46:15 344

转载 事务的4种隔离级别

摘要数据库事务的隔离级别有4种,解决并发操作中出现的各种问题,由低到高分别为:Read uncommitted(读未提交)Read committed(读已提交),解决脏读Repeatable read(可重复读),解决不可重复读Serializable(串行化)。解决幻读大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。Mysql的默认隔离级别是Repeatable read。Read uncommitted读未提交,顾名思义,就

2021-04-20 13:39:11 213

原创 nginx传递真实客户端ip

问题我们在用nginx做反向代理的时候,通常会遇到一个问题:服务端解析请求时拿到的都是nginx代理服务器的ip,而不是真实的客户端ip这对我们的业务处理或许存在一些问题,比如鉴权和限流等反向代理HTTP这里介绍nginx在反向代理http服务时如何获取客户端真实ip配置中添加proxy_set_header Remote_addr $remote_addr;完整示例配置如下:worker_processes auto;error_log /var/log/nginx/error.

2021-04-08 17:58:16 3101

转载 Go中的内存对齐

在 Go 中恰到好处的内存对齐

2021-03-29 11:31:15 287

原创 从一段可能卡死的代码出发讲讲goroutine的调度和GC

package mainimport ( "fmt" "runtime")func main() { var i byte go func() { for i = 0; i <= 255; i++ { } }() fmt.Println("Dropping mic") // Yield execution to force executing other goroutines runtime.Go

2021-03-26 14:37:42 581 1

原创 Golang中的逃逸分析

本节讨论golang声明变量时,是放在栈上还是堆上的问题引言package mainfunc foo(arg_val int)(*int) { var foo_val int = 11; return &foo_val;}func main() { main_val := foo(666) println(*main_val)}编译运行$ go run pro_1.go 11这段代码在golang中是可以正常运行的,但了解C/C++的

2021-03-11 15:44:39 738 1

原创 nginx实战

nginx概述Nginx,一种高性能的http服务器软件。俄国人发明,与httpd相比,它的特点是并发能力强,资源消耗小。其他常见的http服务端软件还有:Httpd和IISHttpd就是Apache,IIS是“微软”的产品,是Windows Server中默认提供的http服务器。nginx安装centos通过nfinx官网的yum源安装稳定版1.安装先决条件:sudo yum i...

2021-03-08 17:28:54 151

转载 Go指针的使用限制和突破之路-unsafe

转载:Go指针的使用限制和突破之路这篇文章跟大家聊一下 Go 语言指针这个话题,相较于 C 而言,Go 语言在设计时为了使用安全给指针在类型和运算上增加了限制,这让Go程序员既可以享受指针带来的便利,又避免了指针的危险性。除了常规的指针外,Go 语言在 unsafe 包里其实还通过 unsafe.Pointer 提供了通用指针,通过这个通用指针以及 unsafe 包的其他几个功能又让使用者能够绕过 Go 语言的类型系统直接操作内存进行例如:指针类型转换,读写结构体私有成员这样操作。网管觉得正是因为功能强大

2021-03-03 15:54:42 278 1

原创 Rabbitmq(6):RPC

引言在rabbitmq队列使用的公平分发,我们学习了如何使用工作队列在多个worker之间分配耗时的任务。但是,如果我们需要在远程计算机上运行函数并等待结果怎么办?这种模式通常称为远程过程调用或RPC。相关概念可参考rpc概述在本教程中,我们将使用RabbitMQ构建一个RPC系统:客户端和可伸缩RPC服务器。由于我们没有值得分配的耗时任务,因此我们将创建一个虚拟RPC服务,该服务返回斐波那契数。细节提要:服务端声明一个队列,接收消息,计算所对应的斐波那契数,然后根据消息的id信息,将结果发到客户端

2021-02-03 15:36:22 378

原创 RabbitMQ(5):Topic

引言在之前的发布订阅内容中,我们提到了有几种交换器可以用,他们分别是direct, topic, headers 和 fanout。direct和fanout在前面的文章中已经知道了功能及怎么用。direct交换器能让消费者选择自己想要的消息,但这种消息是完全确定的,没有条件的过滤。针对这种需要根据条件灵活选择的情况,可以通过topic交换器来实现topic交换器(主题交换器)发送到topic交换器的消息不能具有随意的routing_key——它必须是单词列表,以点分隔。这些词可以是任何东西,但通

2021-02-02 17:00:27 152

原创 RabbitMQ(4):路由

引言在rabbitmq的发布订阅一节中,我们构建了一个简单的日志记录系统。我们能够向许多接收者广播日志消息。但是每个接收者接收到的消息都是相同的。在有一些场景中,我们希望只接收自己想要的,比如日志系统中我们选择不将非error级别的日志保存在文件中。本节内容中,我们将向它添加一个特性-我们将使它能够只订阅消息的一个子集。例如,我们将只能将关键错误消息定向到日志文件(以节省磁盘空间),同时仍然能够在控制台上打印所有日志消息。绑定...

2021-02-02 11:20:04 110

原创 RabbitMQ(3):发布订阅

引言在前面我们学习了RabbitMQ简介,安装及简单使用:docker-compose安装rabbitmq及简单使用Hello World还有队列的简单使用,循环调度,消息确认,消息持久化,公平分发:队列使用在之前的两个教程中,我们创建了一个工作队列。工作队列背后的假设是每个任务只传递给一个工人。在这一部分中,我们将做一些完全不同的事情——我们将向多个消费者传递同一个消息。这就是所谓的“订阅/发布模式”。为了说明这种模式,我们将构建一个简单的日志系统。它将由两个程序组成——第一个程序将发出日志消息,

2021-02-01 16:06:34 225

原创 RabbitMQ(2): 队列使用

引言在上一节【RabbitMQ(一)】docker-compose安装rabbitmq及简单使用Hello World中,我们介绍了Rabbitmq的安装和简单操作。达到了生产者发送一个Hello World之后,消费者便能接收到一个Hello World的效果。如果有多个消费者同时在接收消息,消息会如何分发呢?消费者在处理消息的时候宕机了,那这个消息怎么办呢?带着这些问题,开始RabbitMQ第二节的学习内容,队列的使用及策略循环调度使用任务队列的优点之一是能够轻松并行化工作。如果我们的工

2021-01-29 14:31:24 313 2

原创 RabbitMQ(1): docker-compose安装rabbitmq及简单使用Hello World

编写docker-compose.ymlversion: '3'services: rabbitmq: image: rabbitmq:3.8.3-management container_name: rabbitmq restart: always hostname: myRabbitmq ports: - 15672:15672 - 5672:5672 volumes: - ./data:/var/lib/rab

2021-01-28 14:57:59 2403

原创 Redis 缓存穿透、击穿、雪崩

常见问题缓存击穿缓存雪崩缓存击穿具体说明缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库。解决办法参数校验最基本的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。缓存无效key如果缓存和数据库都查不到某个 key

2021-01-25 11:29:45 140

原创 信号量的使用方法

简介信号量是并发编程中常见的一种同步机制,在需要控制访问资源的线程数量时就会用到信号量使用场景在需要控制访问资源的线程数量时就会需要信号量我来举个例子帮助你理解。假设我们有一组要抓取的页面,资源有限最多允许我们同时执行三个抓取任务,当同时有三个抓取任务在执行时,在执行完一个抓取任务后才能执行下一个排队等待的任务。当然这个问题用Channel也能解决,不过这次我们使用Go提供的信号量原语来解决这个问题,代码如下:package mainimport ( "context" "fm

2021-01-25 09:26:30 1095

原创 一道关于 goroutine 的面试题

问题package mainimport ( "fmt" "time")func main() { ch1 := make(chan int) go fmt.Println(<-ch1) ch1 <- 5 time.Sleep(1 * time.Second)}问:上面代码输出什么?是5还是别的呢?分析如果代码换成如下:package mainimport ( "fmt" "time")func main() { ch1 := make(c

2021-01-21 18:10:15 379

原创 检查表达式中的括号是否匹配

借用栈结构我们用栈来保存未匹配的左括号,从左到右依次扫描字符串。当扫描到左括号时,则将其压入栈中;当扫描到右括号时,从栈顶取出一个左括号。如果能够匹配,比如“(”跟“)”匹配,“[”跟“]”匹配,“{”跟“}”匹配,则继续扫描剩下的字符串。如果扫描的过程中,遇到不能配对的右括号,或者栈中没有数据,则说明为非法格式。当所有的括号都扫描完成之后,如果栈为空,则说明字符串为合法格式;否则,说明有未匹配的左括号,为非法格式。package mainimport ( "errors" "fmt")t

2021-01-18 10:43:09 1958

原创 单链表翻转

思路一:借用新的单链表新建一个新的单向链表,从头循环处理旧链表节点,依次插入到新单向链表的头节点,最后用新单向链表的头节点替换旧单向链表的头节点package mainimport ( "fmt")// 链表type LinkList struct { HeadNode *LinkNode}//单向链表节点type LinkNode struct { Data int32 // 链表上的数据 Next *LinkNode // 指针指向下一个数据}// 在头部添加数据,

2021-01-18 10:09:11 124 1

原创 redis实现分布式锁

分布式锁是什么?为什么需要它分布式锁就是在两个系统进程(可以不在同一台机器上)之间的加锁操作,使其实现原子操作上面这样讲,可能还是不明白。下面从一个案例出发:比如常见的电商系统,一般采用微服务架构。我们以其中的库存服务为例。在并发量不大的时候,一个库存服务就可以搞定。如下图,系统A代表一个库存服务:但是在并发量上来之后,我们需要水平扩展库存微服务。假设现在有两个库存服务,同一样商品有两个订单生成。但是这样一来会产生一个问题:假如某个时刻,redis里面的某个商品库存为1,此时两个请求同时到来,

2021-01-14 14:51:12 121

原创 redis深度历险学习笔记--基础与应用篇

redis应用场景缓存和分布式锁等等举例:记录帖子点赞数,评论数和点击数(hash)记录用户的帖子ID列表(排序),便于快速显示用户的帖子列表(zset)记录帖子的标题,摘要,作者和封面信息,用于列表页展示(hash)记录帖子的相关文章ID,根据内容推荐相关帖子(list)如果帖子的ID是整数自增的,可以使用Redis来分配帖子ID(计数器)等等redis安装有docker方式,Github源码编译方式,直接安装方式这里推荐docker安装方式,docker-compose搭建re

2021-01-13 22:58:52 130

原创 数组实现LRU缓存淘汰策略

LRU简介LRU 缓存淘汰算法就是一种常用策略。LRU 的全称是 Least Recently Used,也就是说我们认为最近使用过的数据应该是是「有用的」,很久都没用过的数据应该是无用的,内存满了就优先删那些很久没用过的数据。过程思考我的思路是这样的:我们维护一个数组,越靠近数组尾部的结点是越早之前访问的。当有一个新的数据被访问时,我们遍历数据1.如果此数据之前已经被缓存在数组中了,我们遍历得到这个数据,返回成功,并将其从原来的位置删除,然后再插入到数组的头部。2.如果此数据没有在缓存在数组中,

2021-01-13 16:44:55 842 2

原创 循环队列

package mainimport ( "errors" "fmt" "strconv")// 循环队列实现方法type loopQueue struct { queues []interface{} front int //队首 tail int //队尾 length int //队伍长度 capacity int //队伍容量}// 创建循环队列,可指定容量,默认为0func NewLoopQueue(args ...int) *loopQu

2021-01-11 11:15:00 115

原创 windows环境下golang使用protobuf

默认情况下IDE goland 是不支持protobuf协议文件类型".proto"的,为了更快高效的编写proto文件中的代码下面我们介绍一款插件让其支持,该插件支持关键字高亮及语法错误提示1、File->Settings->Plugins->Browse repositories->输入protobuf support->install,并重启IDE2、file->Settings->Editor->File Types,找到Protobuf,注册支

2021-01-04 22:37:16 953 1

原创 atomic原子操作

atomic原子操作在一些场景下,相比于其他的并发原语,性能更优举个例子:假设你想在程序中使用一个标志(flag,比如一个 bool 类型的变量),来标识一个定时任务是否已经启动执行了,你会怎么做呢?我们先来看看加锁的方法。如果使用 Mutex 和 RWMutex,在读取和设置这个标志的时候加锁,是可以做到互斥的、保证同一时刻只有一个定时任务在执行的,所以使用 Mutex 或者 RWMutex 是一种解决方案。其实,这个场景中的问题不涉及到对资源复杂的竞争逻辑,只是会并发地读写这个标志,这类场景就适合使用

2021-01-04 14:54:27 216

原创 正在执行的goruntine发生阻塞,golang调度策略

减少阻塞如果正在执行的 Goroutine 阻塞了线程 M 怎么办?P 上 LRQ 中的 Goroutine 会获取不到调度么?在 Go 里面阻塞主要分为一下 4 种场景:场景 1:由于原子、互斥量或通道操作调用导致 Goroutine 阻塞,调度器将把当前阻塞的 Goroutine 切换出去,重新调度 LRQ 上的其他 Goroutine;场景 2:由于网络请求和 IO 操作导致 Goroutine 阻塞,这种阻塞的情况下,我们的 G 和 M 又会怎么做呢?Go 程序提供了网络轮询器(N

2021-01-04 14:12:38 2129

转载 GMP调度11大场景全解析

(1) 场景 1P 拥有 G1,M1 获取 P 后开始运行 G1,G1 使用 go func() 创建了 G2,为了局部性 G2 优先加入到 P1 的本地队列。(2) 场景 2G1 运行完成后 (函数:goexit),M 上运行的 goroutine 切换为 G0,G0 负责调度时协程的切换(函数:schedule)。从 P 的本地队列取 G2,从 G0 切换到 G2,并开始运行 G2 (函数:execute)。实现了线程 M1 的复用。(3) 场景 3假设每个 P 的本地队列只能存 3 个

2020-12-31 16:51:51 627

原创 B+树

简介B+树是应文件系统所需而产生的B树的变形树,那么可能一定会想到,既然有了B树,又出一个B+树,那B+树必然是有很多优点的,其中最重要的一点就是有者比B-tree更高的查询性能B+树的特征和B-树相比较,具备一些新的特征:有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点;所有的叶子结点中包含了全部元素的信息,及指向含有这些元素记录的指针,且叶子结点本身依元素的大小自小而大的顺序链接。 (而B树的叶子节点并没有包括全部需要查找的

2020-12-25 14:19:19 269

原创 B-树详解(二)

引言在B-树详解(一)的部分,我们从数据库索引出现,比较了二叉查找树和B-Tree的查找效率,B-Tree通过建少磁盘IO提高查找效率。在第一部分我们对B-树的定义和整体有了一定的认识,下面就重点看看B-Tree查找,插入,和删除的具体过程。定义B树是一种平衡的多分树,通常我们说m阶的B树,它或者是空树,或者必须满足如下条件:如果根不是叶节点,则根至少有两个子节点(不然的话就成单支了),有[1,m-1]个元素每个中间节点(不是根节点和叶子节点)都包含[math.ceil(m/2)-1,m-1]个

2020-12-24 13:54:31 212

原创 B-树详解(一)

引言前面我们已经讲到很多的树,比如普通二叉树,二叉堆,二叉查找树,平衡二叉树等。那现在有一个问题,这么多的树都是用来干什么的?其实啊,任何事物都有着发展的必然性,都是为了解决问题。而随着问题规模和深度的不断加深,对应的解决方案也随之发展。这些树大多都是为了解决查找效率,或者是保证查找结果的有序性。实际场景中,无非读和写(删除和更新算是写的一种)。针对写操作,更看重的是稳定性,正确写是第一位的,写的速度是其次,因为生产数据的来源流量也肯定不是很大,多花时间来写是ok的。但是读就是持续追求效率的方向,历史数据

2020-12-23 17:04:37 214

原创 排序(二)--O( n^2)

冒泡排序依次优化func bubble_sort_v1(arrry []int) { for i := 0; i < len(arrry)-1; i++ { for j := 0; j < len(arrry)-1-i; j++ { if arrry[j] > arrry[j+1] { temp := arrry[j] arrry[j] = arrry[j+1] arrry[j+1] = temp } } }}func bubble

2020-12-16 23:13:18 89

原创 平衡二叉(AVL)树

在前面的系列文章中,我们讲过了二叉查找树的相关知识和实现(二叉树(一)–概述(附二叉搜索树实现))。在文章的最后,我们总结了二叉查找树的查找时间复杂度:对于一个节点分布相对均衡的二叉查找树来说,如果节点总数是n,那么搜索节点的时间复杂度是O(logn),和树的深度是一样的。但对于极端情况(每次插入数据大小都是增大的,或者减小的),外形上看就只有一半的树,查找时间复杂度就会退化成O(n)的为了解决二叉查找树查找时间复杂度退化的问题,需要二叉数据的自平衡,也就是本节要讲的平衡二叉树。通过自身的平衡调整

2020-12-15 14:24:18 888

原创 Once:一个简约而不简单的并发原语

Once 可以用来执行且仅仅执行一次动作,常常用于单例对象的初始化场景。Once 的使用场景sync.Once 只暴露了一个方法 Do,你可以多次调用 Do 方法,但是只有第一次调用 Do 方法时 f 参数才会执行,这里的 f 是一个无参数无返回值的函数func main() { var once sync.Once // 第一个初始化函数 f1 := func() { fmt.Println("in f1") } once.Do(f1) //

2020-12-14 11:09:05 158

原创 Cond--条件变量并发原语

先不讲这个cond是什么,我们从一个场景出发:有一个慈善机构需要募集善款,目标比方说是100万。那就需要发一个公告,说需要筹集善款100万。群众看到公告就开始捐钱,每个人能力不一样,有的可能1万,有的可能2万,有的人看着不够再捐一笔。为了更快的筹够善款,那肯定是人越多越好,那在计算机世界就是用上所有的cpu资源。很快100万就够了,慈善机构就再发一个公告,说钱够了,大家不用捐了。那这个消息传递可能有延迟,收到的钱比100万多那也没办法,收多少算多少呗,只要能如实公示,正确使用就行了把上诉场景抽象一下,这

2020-12-11 17:37:34 335

原创 golang中的互斥锁

mutex是golang提供的基础并发原语,可以帮助我们处理多goruntine并发访问共享资源的问题。每个goruntine都要再获取到锁之后才能操作共享资源,完成操作释放锁,保证了共享资源的读写安全性。但这种方式也可能带来一些问题:一些悲惨的goruntine一直获取不到锁,导致业务逻辑不能继续完整执行,这种问题被称为"饥饿问题"为了解决这种问题,mutex在长时间的发展中不断完善,目前有两种操作模式,防止饥饿问题的出现饥饿模式和正常模式正常模式当前的mutex只有一个goruntine来获

2020-12-09 18:34:46 536

原创 golang的检测并发访问共享资源是否有问题的工具--race detetor

Go race detector 是基于 Google 的 C/C++ sanitizers 技术实现的,编译器通过探测所有的内存访问,加入代码能监视对这些内存地址的访问(读还是写)。在代码运行的时候,race detector 就能监控到对共享变量的非同步访问,出现 race 的时候,就会打印出警告信息。这个技术在 Google 内部帮了大忙,探测出了 Chromium 等代码的大量并发问题。Go 1.1 中就引入了这种技术,并且一下子就发现了标准库中的 42 个并发问题。现在,race detector

2020-12-09 17:45:08 701

原创 golang中的GC

GOLANG的垃圾回收机制现在go1.14所用的是三色标志法和GC混合写屏障GC版本变化Go V1.3 标记清除(mark and sweep)Go V1.5 三色标记法Go V1.8 加入混入写屏障

2020-12-03 21:33:54 1604 1

原创 排序概述(一)

时间复杂度分类1.时间复杂度为O(n^2)的排序算法冒泡排序选择排序插入排序希尔排序(希尔排序比较特殊,它的性能略优于O(n^2),但又比不上O(nlogn))2.时间复杂度为O(logn)的排序算法快速排序归并排序堆排序3.时间复杂度为线性的排序算法计数排序桶排序基数排序稳定性分类排序算法根据稳定性,划分为稳定排序和不稳定排序。如果值相等的元素在排序后仍然保持着排序前的顺序,则是稳定排序如果值相等的元素在排序后打乱了排序前的顺序,则是不稳定排序1.稳定排序

2020-12-02 23:06:21 69

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除