golang的sql.DB的一些注意事项及读写锁的总结

sryan · · 1970 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

最近在写一个用于存储protobuf配置的配置管理服务,业务逻辑不难,2天就搞定,但是后续bug不少,也踩了很多坑,在这里记录下。


首先,一直以为golang内的sql模块是单链接的,所以一开始在每个goroutine内都open了一个DB,并写了一个连接池进行管理。后续发现是多此一举,白白写了好多代码。golang的sql模块自带连接池功能,在执行sql语句的时候才会分配连接,执行完毕后归还给连接池,所以假设用golang的sql模块,一个程序一个DB就行了。


既然有连接池的支持,那么也要注意千万不要泄露连接池的连接。假设你采用Query来执行查询语句,那么会返回一个sql.Rows结构,这个结构会占用一个连接,只有在遍历完才会自动关闭,所以最好是获得了Rows后执行一次Rows的Close方法,多次Close是没事的。


然后,因为被上级否定了使用transaction的想法,只能在程序内进行事务控制。一开始整个sql执行model共享一个读写锁,在执行性能测试的情况下,读的tps在3k左右,性能还行,可是写却只是200。这是无法接受的,后来仔细分析了下代码,在写前面锁了写,那么并行的几个routine会等待占有锁的那个routine写入完毕才会有第二个routine进行写操作,就等于白白的排队了,而测试用例是insert新的记录而已,不会有冲突的问题。现在想想一个锁虽然写起来方便,但是性能影响很大,于是今天写了一个新的读写锁管理器,绑定特定的key,每一个key在当前key的锁被占用的情况下,会返回被占用的锁,并且将引用计数值+1,假设没有对应的锁,则返回新的锁。释放锁的时候,判定当前key的锁的引用值,假设已经为0了,说明没有被其它routine进行锁wait,则销毁这个锁,否则引用计数值-1。


这样的话,将不同的sql生成一个key,采用这个key来进行写冲突管理,当两个sql有相同的key的时候,则会进行锁竞争,假设key不相同,则不会有竞争。同时采用引用计数来避免读写锁的泄露,对长期稳定运行服务器有好处。

读操作的话,则当前没有写锁的情况下,则直接进行读取,所以读的性能不会有很大的影响。


在这个新的锁的设计下,tps从200提升到了1200,算是可接受的范围了。这个方案的关键点在于key的生成,在于提取每个sql操作影响的行,只要能得到这个key,则生成读写锁将十分方便。


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:sryan

查看原文:golang的sql.DB的一些注意事项及读写锁的总结

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1970 次点击  
加入收藏 微博
1 回复  |  直到 2000-01-01 00:00:00
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传