十二 092014
 

查阅文档时看到一篇2013年12月3日写的文章,一直忘了贴出来,放在硬盘角落里厚厚一层灰了。
———————————————————————————
SQLite是一种嵌入式数据库,没有server进程,每个数据库均为单文件存储,因此在有多线程并发读写同一个数据库时,会因为文件读写锁造成并发写入或读取的失败率上升。
为了解决这个问题,基本的思路就是让存在多线程并发写入的情况收敛为顺序写入。在多线程这个策略无法更改的基础上,将数据库拆分成每个线程对应一个单独的文件就是比较适宜的解决方案。
我们的业务基本情况是有数量级为百或千的PC,这里假定为机器000到999,每30秒发送五组不同的数据,对应机器的五个不同指标,这里假定为指标A-E。为了方便归档和减少单个数据库文件的大小,需要按日期将数据库文件归类,每日、不同机器的数据存储到不到的数据库文件中。因此储存的目的目录结构为:
1111
对于每一项指标,都是通过一个异步队列到达存储层的,因此在数据到达的时间上并不一定按照指标实际产生的实际为序。这就使得在日期切换时,并不能保证在日期戳为20131203的第一个数据抵达后,不会再有日期戳为20131202的数据抵达。因此必须有一个缓冲时间段,在缓冲器内对同一机器的同一指标同时维护两个数据库连接,待旧日期戳数据包基本处理完毕后关闭旧的数据库连接。
为了达到这一效果,要在内存中维护一个数据结构,该结构保存了一个单调递增的日期戳和一个数据库连接池,连接池以日期哈希的形式保存数据库连接。程序启动时初始化该数据结构,每次有数据需要存储时,调用该结构的GetLink方法得到本数据包应该对应的数据库连接。在日期更替时,数据结构中的日期戳会指向最新的日期,防止切换过程中可能出现的连接池震荡。

type DbLink struct {
	Today string
	Changing bool
	Links map[string]*sql.DB
}

func NewDbLink(date string) (link *DbLink) {
	links := make(map[string]*sql.DB)
	link = &DbLink{
		date,
		false,
		links,
	}
	return
}

func (link *DbLink) GetLink(date string, hardware_addr string, indicator string) (dbLink *sql.DB, err error) {
	key := date + "_" + hardware_addr
	dbLink, ok := link.Links[key]
	// 如果已经存在,则认为没有日期变更,且数据库连接已经打开
	if ok {
		// fmt.Println("bingo!") //命中已经打开的数据库连接
		return dbLink, nil
	} else {
		// 否则为新的日期打开新的数据库连接,并延时关闭原有日期对应的数据库连接,且删除其在本结构体中的注册条目
		var dbPath, dbSourceName string
		dbPath = "../db/" + date + "/" + strings.Replace(hardware_addr, ":", "_", -1) + "/"
		dbSourceName = dbPath + indicator + ".db"
		os.MkdirAll(dbPath, 0666)
		link.Changing = true
		inComingDate, _ := strconv.Atoi(date)
		currentDate, _ := strconv.Atoi(link.Today)
		if inComingDate > currentDate {
			// 仅当后来的日期比保存的日期更晚时,更新结构体中的Today值
			link.Today = date
		}
		newLink, err := sql.Open("sqlite3", dbSourceName)
		link.Links[key] = newLink
		if err != nil {
			return nil, err
		}
		createTable(indicator, newLink)
		go func() {
			c := time.Tick(5 * time.Minute)
			for _ = range c {
				for k, v := range link.Links {
					// 如果缓存中有非Today的日期,表示已经过期,可以执行延时关闭
					if !strings.HasPrefix(k, link.Today) {
						v.Close()
						fmt.Println(k, "to be deleted")
						delete(link.Links, k)
						link.Changing = false
					}
				}
			}
		}()
		return newLink, nil
	}
	return nil, nil
}

 Leave a Reply

(必须填写)

(必须填写,邮件地址不会被泄露)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>