一个NULL引发的血案

go sql.stmt query 发生了一个NULL值,所以发现了error, 发现服务不停的初始化sql stmt, 导致连接数过多,服务就变得很慢。

首先,我在初始化的之前,要判断这个是否是NULL(见下面列表5)导致的,并非是sql请求出错。 我为啥要全局变量保存sql.Stmt, 导致要处理sql.Stmt出错。

go prepare 的问题(见下面列表2)导致数据库连接数太大,所以我使用全局变量保存用prepare 方法创建的sql.Stmt(见下面列表3,但是根据列表2,其实没有解决问题,  除非列表4),同时就要考虑出现错误的时候重新初始化。初始化的时候就涉及到互斥的问题, 那就要加锁(见下面列表1)。但是锁不能乱加,因为后台是要支持并发的,如果都加写锁。就会响应很慢了。所以要加读锁,重新初始化的时候,解读锁加写锁,但是加锁的作用是线程与线程之前,并非是for 语句,所以你如果有for语句,要考虑是否要break。加锁的范围不要很大,因为太大不好控制(出现嵌套加锁,其实无关的代码占用了释放锁的时间),而且你要释放锁


1. 读写锁

http://www.alexedwards.net/blog/understanding-mutexes

func (rw *RWMutex) Lock()

Lock locks rw for writing. If the lock is already locked for reading or writing, Lock blocks until the lock is available. To ensure that the lock eventually becomes available, a blocked Lock call excludes new readers from acquiring the lock.

不能读也不能写

Unlock unlocks rw for writing. It is a run-time error if rw is not locked for writing on entry to Unlock.

As with Mutexes, a locked RWMutex is not associated with a particular goroutine. One goroutine may RLock (Lock) an RWMutex and then arrange for another goroutine to RUnlock (Unlock) it.

We can achieve this using RWMutex, a reader/writer mutual exclusion lock which allows any number of readers to hold the lock or one writer.  ---  可以有多个读操作,或者一个写操作。

如果不加对应的锁,然后解锁的话会引发错误。会引发错误。

但是已经加了一个读锁,没有解锁又加一个写锁,会造成死锁。就是挂住,程序不会动了。

有的会打印: fatal error: all goroutines are asleep - deadlock!


2. 使用sql prepare不能很好复用,它可能自己重新生成prepare语句,会导致泄露。

http://go-database-sql.org/prepared.html

Here’s how it works:

  1. When you prepare a statement, it’s prepared on a connection in the pool.
  2. The Stmt object remembers which connection was used.
  3. When you execute the Stmt, it tries to use the connection. If it’s not available because it’s closed or busy doing something else, it gets another connection from the pool and re-prepares the statement with the database on another connection.

Because statements will be re-prepared as needed when their original connection is busy, it’s possible for high-concurrency usage of the database, which may keep a lot of connections busy, to create a large number of prepared statements. This can result in apparent leaks of statements, statements being prepared and re-prepared more often than you think, and even running into server-side limits on the number of statements.


3. sql.Stmt 是可并发的

type Stmt

type Stmt struct {
// contains filtered or unexported fields
}

Stmt is a prepared statement. A Stmt is safe for concurrent use by multiple goroutines.

http://go-database-sql.org/retrieving.html


4.  go 1.4 fixed bug.

prepare stmt

http://studygolang.com/articles/1795


5. 数据表里默认值为NULL

http://www.cnblogs.com/javathread/archive/2012/01/05/2634719.html

11. 尽可能的使用 NOT NULL

除非你有一个很特别的原因去使用 NULL 值,你应该总是让你的字段保持 NOT NULL。这看起来好像有点争议,请往下看。

首先,问问你自己“Empty”和“NULL”有多大的区别(如果是INT,那就是0和NULL)?如果你觉得它们之间没有什么区别,那么你就不要使用NULL。(你知道吗?在 Oracle 里,NULL 和 Empty 的字符串是一样的!)

不要以为 NULL 不需要空间,其需要额外的空间,并且,在你进行比较的时候,你的程序会更复杂。 当然,这里并不是说你就不能使用NULL了,现实情况是很复杂的,依然会有些情况下,你需要使用NULL值。

下面摘自MySQL自己的文档:    

“NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.”      

上一篇:【UIView与控件】


下一篇:redis 集群创建常见几个问题