-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest.go
More file actions
72 lines (63 loc) · 2.36 KB
/
Copy pathtest.go
File metadata and controls
72 lines (63 loc) · 2.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// 1.1 将数据持久化到文件
func SaveData1(path string, data []byte) error {
fp, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer fp.Close()
_, err = fp.Write(data)
return err
}
// 问题
// 1. 在更新文件前会截断文件。文件需要被并发读取怎么办。
// 2.根据写入数据的大小,向文件写入内容可能不是原子操作。并发读取文件时,可能会读到部分更新的数据。
// 3. 数据何时被写入磁盘,在写系统调用后,数据可能仍存在操作系统的页面缓存中。如果系统崩溃并重启,文件会处于什么状态
// 1.2原子冲命名
func SaveData2(path string, data []byte) error {
tmp := fmt.Sprintf("%s.tmp.%d", path, os.Getpid())
fp, err := os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer fp.Close()
_, err = fp.Write(data)
if err != nil {
os.Remove(tmp)
return err
}
return os.Rename(tmp, path)
}
//问题 因为它无法控制数据何时被持久化到磁盘,并且文件的元数据(文件大小)
// 可能在数据之前被持久化到磁盘,这在系统崩溃后可能会损坏文件。
// (你可能注意到,一些日志文件在停电后会出现零值,这就是文件损坏的迹象。)
// 1.3 使用fsync确保数据持久化
func SaveData3(path string, data []byte) erroer {
_, err = fp.Write(data)
if err != nil {
os.Remove(tmp)
return err
}
err = fp.Sync()
if err != nil {
os.Remove(tmp)
return err
}
return os.Rename(tmp, path)
}
// 我们已经将数据刷新到磁盘,但元数据呢?我们是否也应该对包含该文件的目录调用 fsync 呢?
// 1.4 只追加日志
func LogCreate(path string) (*os.File, error) {
return os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0664)
}
func LogAppend(fp *os.File, line []byte) error {
buf := []byte(line)
buf = append(buf, '\n')
_, err := fp.Write(buf)
if err != nil {
return err
}
return fp.Sync()
}
// 只追加日志的优点是它不会修改现有数据,也不需要处理重命名操作,因此更不容易损坏。但是,仅靠日志不足以构建一个数据库。
// 数据库使用额外的 “索引” 来高效地查询数据。对于一堆任意顺序的记录,使用日志只能进行暴力查询。
// 日志如何处理已删除的数据?日志不能无限增长。