第一段:介绍go标准库中的bufio
最近用golang写了一个处理文件的脚本,由于其中涉及到了文件读写,开始使用golang中的 io 包,后来发现golang 中提供了一个bufio的包,使用这个包可以大幅提高文件读写的效率,于是在网上搜索同样的文件读写为什么bufio 要比io 的读写更快速呢?根据网上的资料和阅读源码,以下来详细解释下bufio的高效如何实现的.
bufio 包介绍
bufio包实现了有缓冲的I/O.它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象.
以上为官方包的介绍,在其中我们能了解到的信息如下:
bufio 是通过缓冲来提高效率
简单的说就是,把文件读取进缓冲(内存)之后再读取的时候就可以避免文件系统的io 从而提高速度.同理,在进行写操作时,先把文件写入缓冲(内存),然后由缓冲写入文件系统.看完以上解释有人可能会表示困惑了,直接把 内容-文件 和 内容-缓冲-文件相比, 缓冲区好像没有起到作用嘛.其实缓冲区的设计是为了存储多次的写入,最后一口气把缓冲区内容写入文件.下面会详细解释
bufio 封装了io.Reader或io.Writer接口对象,并创建另一个也实现了该接口的对象
io.Reader或io.Writer 接口实现read() 和 write() 方法,对于实现这个接口的对象都是可以使用这两个方法的
注明:介绍内容来自博主 LiangWenT
,原文链接: ,在查找资料时,发现这篇博客的内容很好理解
bufio包实现了缓存IO.它包装了io.Reader和io.Write对象,创建了另外的Reader和Writer对象,它们也实现了io.Reader和io.Write接口,具有缓存.注意:缓存是放在主存中,既然是保存在主存里,断电会丢失数据,那么要及时保存数据.
第二段:常用内容
①.、Reader类型
NewReaderSize
NewReader
Peek
Peek返回缓存的一个切片,该切片引用缓存中前n个字节的数据,该操作不会将数据读出,只是引用,引用的数据在下一次读取操作之前有效的.如果切片长度小于n,则返回一个错误信息说明原因.如果n大于缓存的总大小,则返回ErrBufferFull.
Read
Read从b中数据到p中,返回读出的字节数和遇到的错误.如果缓存不为空,则只能读出缓冲中的数据,不会从底层io.Reader中提取数据,如果缓存为空,则:
①.、len(p) = 缓存大小,则跳过缓存,直接从底层io.Reader中读出到p中
Buffered
Buffered返回缓存中未读取的数据的长度.
Discard
Discard跳过后续的n个字节的数据,返回跳过的字节数.
Writer类型和方法
write结构
NewWriteSize
NewWrite
WriteString
WriteString功能同Write,只不过写入的是字符串
WriteRune
Flush
Available
Available 返回缓存中未使用的空间的长度
Buffered返回缓存中未提交的数据长度
Reset
Reset将b的底层Write重新指定为w,同时丢弃缓存中的所有数据,复位所有标记和错误信息.相当于创建了一个新的bufio.Writer.
GO中还提供了Scanner类型,处理一些比较简单的场景.如处理按行读取输入序列或空格分隔的词等.
内容来自:
参考链接:
①.)
①.、反射可以在运行时 动态获取变量的各种信息 ,比如变量的类型、类别;
①.、不知道接口调用哪个函数,根据传入参数在运行时确定调用的具体接口,这种需要对函数或方法反射.
例如以下这种桥接模式:
示例第一个参数funcPtr以接口的形式传入函数指针,函数参数args以可变参数的形式传入,bridge函数中可以用反射来动态执行funcPtr函数.
①.、reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型.
①.、reflect.Value.Kind,获取变量的 类别(Kind) ,返回的是一个 常量 .在go语言文档中:
示例如下所示:
输出如下:
Kind的范畴要比Type大.比如有Student和Consumer两个结构体,他们的 Type 分别是 Student 和 Consumer ,但是它们的 Kind 都是 struct .
如果是x是float类型的话,也是要用reflect.Value(x).Float().但是如果是struct类型的话,由于type并不确定,所以没有相应的方法,只能 断言.
原文:【 】
如果有解答的不对的,麻烦各位在评论写出来~
go的调度原理是基于GMP模型,G代表一个goroutine,不限制数量;M=machine,代表一个线程,最大1万,所有G任务还是在M上执行;P=processor代表一个处理器,每一个允许的M都会绑定一个G,默认与逻辑CPU数量相等(通过runtime.GOMAXPROCS(runtime.NumCPU())设置).
go调用过程:
可以能,也可以不能.
因为go存在不能使用==判断类型:map、slice,如果struct包含这些类型的字段,则不能比较.
这两种类型也不能作为map的key.
类似栈操作,后进先出.
因为go的return是一个非原子性操作,比如语句 return i ,实际上分两步进行,即将i值存入栈中作为返回值,然后执行跳转,而defer的执行时机正是跳转前,所以说defer执行时还是有机会操作返回值的.
select的case的表达式必须是一个channel类型,所有case都会被求值,求值顺序自上而下,从左至右.如果多个case可以完成,则会随机执行一个case,如果有default分支,则执行default分支语句.如果连default都没有,则select语句会一直阻塞,直到至少有一个IO操作可以进行.
break关键字可跳出select的执行.
goroutine管理、信息传递.context的意思是上下文,在线程、协程中都有这个概念,它指的是程序单元的一个运行状态、现场、快照,包含.context在多个goroutine中是并发安全的.
应用场景:
例子参考:
waitgroup
channel
len:切片的长度,访问时间复杂度为O(1),go的slice底层是对数组的引用.
扩容:每次扩容slice底层都将先分配新的容量的内存空间,再将老的数组拷贝到新的内存空间,因为这个操作不是并发安全的.所以并发进行append操作,读到内存中的老数组可能为同一个,最终导致append的数据丢失.
共享:slice的底层是对数组的引用,所以呢如果两个切片引用了同一个数组片段,就会形成共享底层数组.当sliec发生内存的重新分配(如扩容)时,会对共享进行隔断.详细见下面例子:
make([]Type,len,cap)
如果要实现map的顺序读取,需要使用一个slice来存储map的key并按照顺序进行排序.
利用map,如果要求并发安全,就用sync.map
要注意下set中的delete函数需要使用 delete(map) 来实现,但是这个并不会释放内存,除非value也是一个子map.当进行多次delete后,可以使用make来重建map.
使用sync.Map来管理topic,用channel来做队列.
参考:
多路归并法:
pre class="vditor-reset" placeholder="" contenteditable="true" spellcheck="false"p data-block="0"(1)假设有K路a href=""数据流/a,流内部是有序的,且流间同为升序或降序;
/p/pre
第一步:
可以分为10段读取,每段读取100M的数据并排序好写入硬盘.
假设写入后的文件为A,B,C...10
第二步:
将A,B,C...10的第一个字符拿出来,对这10个字符进行排序,并将结果写入硬盘,同时记录被写入的字符的文件指针P.
第三步:
重复第二段:三步骤.
go文件读写参考:
保证排序前两个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同的排序叫稳定排序.
快速排序、希尔排序、堆排序、直接选择排序不是稳定的排序算法.
基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法.
head只请求页面的首部.多用来判断网页是否被修改和超链接的有效性.
get请求页面信息,并返回实例的主体.
参考:
这个keepalive_timout时间值意味着:一个http产生的tcp连接在传送完最后一个响应后,还需要hold住keepalive_timeout秒后,才开始关闭这个连接.
tcp的keep alive可通过
修改:
可以
tcp是面向连接的,upd是无连接状态的.
udp使用场景:视频通话、im聊天等.
time-wait作用:
解决办法:
close_wait:
根据业务来啊~
重要指标是cardinality(不重复数量),这个数量/总行数如果过小(趋近于0)代表索引基本没意义,比如sex性别这种.
另外查询不要使用select *,根据select的条件+where条件做组合索引,尽量实现覆盖索引,避免回表.
僵尸进程:
即子进程先于父进程退出后,子进程的PCB需要其父进程释放,但是父进程并没有释放子进程的PCB,这样的子进程就称为僵尸进程,僵尸进程实际上是一个已经死掉的进程.
孤儿进程:
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程.孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作.
子进程死亡需要父进程来处理,那么意味着正常的进程应该是子进程先于父进程死亡.当父进程先于子进程死亡时,子进程死亡时没父进程处理,这个死亡的子进程就是孤儿进程.
但孤儿进程与僵尸进程不同的是,由于父进程已经死亡,系统会帮助父进程回收处理孤儿进程.所以孤儿进程实际上是不占用资源的,因为它终究是被系统回收了.不会像僵尸进程那样占用ID,损害运行系统.
原文链接:
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用.
避免方法:
端口占用:lsof -i:端口号 或者 nestat
cpu、内存占用:top
git log:查看提交记录
git diff :查看变更记录
git merge:目标分支改变,而源分支保持原样.优点:保留提交历史,保留分支结构.但会有大量的merge记录
git rebase:将修改拼接到最新,复杂的记录变得优雅,单个操作变得(revert)很简单;缺点:
git revert:反做指定版本,会新生成一个版本
git reset:重置到某个版本,中间版本全部丢失
etcd、Consul
pprof
节省空间(非叶子节点不存储数据,相对b tree的优势),减少I/O次数(节省的空间全部存指针地址,让树变的矮胖),范围查找方便(相对hash的优势).
explain
其他的见:
方法一:
方法二:
方法1:to_days,返回给的日期从0开始算的天数.