bingoohuang / blog

write blogs with issues
MIT License
177 stars 23 forks source link

海草海草随波飘摇,海草海草浪花里舞蹈 #57

Open bingoohuang opened 5 years ago

bingoohuang commented 5 years ago

image

海草介绍

Seaweedfs是一个非常优秀的golang开发的分布式存储开源项目,专注解决小文件存储。开发者是中国人Chris Lu。Seaweedfs的设计原理是基于Facebook的一篇图片存储系统的论文Facebook-Haystack。目前Star数量7123。据称京东的tiglabs/containerfs其中关键的数据存储部分代码抄袭自海草。

名词术语

来源

术语 解释
master 提供volume=>location 位置映射服务和文件id的序列号
Node 系统抽象的节点,抽象为DataCenter、Rack
Datanode 存储节点,用于管理、存储逻辑卷
DataCenter 数据中心,对应现实中的不同机架
Rack 机架,对应现实中的机柜,一个机架属于特定的数据中心,一个数据中心可以包含多个机架。
Volume 逻辑卷,存储的逻辑结构,逻辑卷下存储Needle,A VolumeServer contains one Store
Needle 逻辑卷中的Object,对应存储的文件, Needle file size is limited to 4GB for now.
Collection 文件集,可以分布在多个逻辑卷上,如果在存储文件的时候没有指定collection,那么使用默认的""
Filer 文件管理器,Filer将数据上传到Weed Volume Servers,并将大文件分成块,将元数据和块信息写入Filer存储区。
Mount 用户空间,当filer与mount一起使用时,filer仅提供文件元数据检索,实际文件内容直接在mount和volume服务器之间读写,所以不需要多个filer

Master Server, Volume Server, Filer Server

image

数据中心、机柜、存储

image

bingoohuang commented 5 years ago

master命令行参数帮助,可以通过weed master -help来查看,通过代码查看更加方便,如下:

mport                   = cmdMaster.Flag.Int("port", 9333, "http listen port")
masterIp                = cmdMaster.Flag.String("ip", "localhost", "master <ip>|<server> address")
masterBindIp            = cmdMaster.Flag.String("ip.bind", "0.0.0.0", "ip address to bind to")
metaFolder              = cmdMaster.Flag.String("mdir", os.TempDir(), "data directory to store meta data")
masterPeers             = cmdMaster.Flag.String("peers", "", "all master nodes in comma separated ip:port list, example: 127.0.0.1:9093,127.0.0.1:9094")
volumeSizeLimitMB       = cmdMaster.Flag.Uint("volumeSizeLimitMB", 30*1000, "Master stops directing writes to oversized volumes.")
volumePreallocate       = cmdMaster.Flag.Bool("volumePreallocate", false, "Preallocate disk space for volumes.")
mpulse                  = cmdMaster.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats")
defaultReplicaPlacement = cmdMaster.Flag.String("defaultReplication", "000", "Default replication type if not specified.")
mMaxCpu               = cmdMaster.Flag.Int("maxCpu", 0, "maximum number of CPUs. 0 means all available CPUs")
garbageThreshold      = cmdMaster.Flag.Float64("garbageThreshold", 0.3, "threshold to vacuum and reclaim spaces")
masterWhiteListOption = cmdMaster.Flag.String("whiteList", "", "comma separated Ip addresses having write permission. No limit if empty.")
masterSecureKey       = cmdMaster.Flag.String("secure.secret", "", "secret to encrypt Json Web Token(JWT)")
masterCpuProfile      = cmdMaster.Flag.String("cpuprofile", "", "cpu profile output file")
masterMemProfile      = cmdMaster.Flag.String("memprofile", "", "memory profile output file")
bingoohuang commented 5 years ago

volume命令行参数帮助,可以通过weed volume -help来查看,通过代码查看更加方便,如下:

v.port = cmdVolume.Flag.Int("port", 8080, "http listen port")
v.publicPort = cmdVolume.Flag.Int("port.public", 0, "port opened to public")
v.ip = cmdVolume.Flag.String("ip", "", "ip or server name")
v.publicUrl = cmdVolume.Flag.String("publicUrl", "", "Publicly accessible address")
v.bindIp = cmdVolume.Flag.String("ip.bind", "0.0.0.0", "ip address to bind to")
v.masters = cmdVolume.Flag.String("mserver", "localhost:9333", "comma-separated master servers")
v.pulseSeconds = cmdVolume.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats, must be smaller than or equal to the master's setting")
v.idleConnectionTimeout = cmdVolume.Flag.Int("idleTimeout", 30, "connection idle seconds")
v.maxCpu = cmdVolume.Flag.Int("maxCpu", 0, "maximum number of CPUs. 0 means all available CPUs")
v.dataCenter = cmdVolume.Flag.String("dataCenter", "", "current volume server's data center name")
v.rack = cmdVolume.Flag.String("rack", "", "current volume server's rack name")
v.indexType = cmdVolume.Flag.String("index", "memory", "Choose [memory|leveldb|boltdb|btree] mode for memory~performance balance.")
v.fixJpgOrientation = cmdVolume.Flag.Bool("images.fix.orientation", false, "Adjust jpg orientation when uploading.")
v.readRedirect = cmdVolume.Flag.Bool("read.redirect", true, "Redirect moved or non-local volumes.")
v.cpuProfile = cmdVolume.Flag.String("cpuprofile", "", "cpu profile output file")
v.memProfile = cmdVolume.Flag.String("memprofile", "", "memory profile output file")

volumeFolders         = cmdVolume.Flag.String("dir", os.TempDir(), "directories to store data files. dir[,dir]...")
maxVolumeCounts       = cmdVolume.Flag.String("max", "7", "maximum numbers of volumes, count[,count]...")
volumeWhiteListOption = cmdVolume.Flag.String("whiteList", "", "comma separated Ip addresses having write permission. No limit if empty.")
bingoohuang commented 5 years ago

filer命令行参数帮助,可以通过weed filer -help来查看,通过代码查看更加方便,如下:

f.masters = cmdFiler.Flag.String("master", "localhost:9333", "comma-separated master servers")
f.collection = cmdFiler.Flag.String("collection", "", "all data will be stored in this collection")
f.ip = cmdFiler.Flag.String("ip", "", "filer server http listen ip address")
f.port = cmdFiler.Flag.Int("port", 8888, "filer server http listen port")
f.grpcPort = cmdFiler.Flag.Int("port.grpc", 0, "filer grpc server listen port, default to http port + 10000")
f.publicPort = cmdFiler.Flag.Int("port.public", 0, "port opened to public")
f.defaultReplicaPlacement = cmdFiler.Flag.String("defaultReplicaPlacement", "000", "default replication type if not specified")
f.redirectOnRead = cmdFiler.Flag.Bool("redirectOnRead", false, "whether proxy or redirect to volume server during file GET request")
f.disableDirListing = cmdFiler.Flag.Bool("disableDirListing", false, "turn off directory listing")
f.maxMB = cmdFiler.Flag.Int("maxMB", 32, "split files larger than the limit")
f.secretKey = cmdFiler.Flag.String("secure.secret", "", "secret to encrypt Json Web Token(JWT)")
f.dirListingLimit = cmdFiler.Flag.Int("dirListLimit", 100000, "limit sub dir listing size")
f.dataCenter = cmdFiler.Flag.String("dataCenter", "", "prefer to write to volumes in this data center")
bingoohuang commented 5 years ago

启动一个测试集群:2 filer(8801-8802) + 3 master(9331-9333) + 3 volume(8081-8083)

  1. 准备日志目录

mkdir -p weed-data cd weed-data

mkdir -p log/filer1 mkdir -p log/filer2 mkdir -p log/master1 mkdir -p log/master2 mkdir -p log/master3 mkdir -p log/volume1 mkdir -p log/volume2 mkdir -p log/volume3

  1. 准备数据目录

filer默认在./filerdb中。

mkdir -p master/mdir1 mkdir -p master/mdir2 mkdir -p master/mdir3 mkdir -p volume/data1 mkdir -p volume/data2 mkdir -p volume/data3

  1. 启动

注意logdir参数的位置,是紧接着在weed之后。 如果在同一台机器上部署,注意logdir区分开,不要放在一起,否则soft link会覆盖。

weed -logdir=log/master1 master -mdir=master/mdir1 -peers=localhost:9331,localhost:9332,localhost:9333 -port=9331 -defaultReplication=001 & weed -logdir=log/master2 master -mdir=master/mdir2 -peers=localhost:9331,localhost:9332,localhost:9333 -port=9332 -defaultReplication=001 & weed -logdir=log/master3 master -mdir=master/mdir3 -peers=localhost:9331,localhost:9332,localhost:9333 -port=9333 -defaultReplication=001 &

weed -logdir=log/volume1 volume -dir=volume/data1 -max=300 -mserver=localhost:9331,localhost:9332,localhost:9333 -port=8081 & weed -logdir=log/volume2 volume -dir=volume/data2 -max=300 -mserver=localhost:9331,localhost:9332,localhost:9333 -port=8082 & weed -logdir=log/volume3 volume -dir=volume/data3 -max=300 -mserver=localhost:9331,localhost:9332,localhost:9333 -port=8083 &

mkdir filer cd filer weed scaffold -config filer -output="." 编辑filer.toml,使用redis来存储filemeta weed -logdir=../log/filer1 filer -port=8801 -master=localhost:9331,localhost:9332,localhost:9333 -defaultReplicaPlacement=001 & weed -logdir=../log/filer2 filer -port=8802 -master=localhost:9331,localhost:9332,localhost:9333 -defaultReplicaPlacement=001 &

bingoo@bogon ~/weed-data> ps -ef|grep weed
  502 13499 13483   0 10:20AM ttys003    0:12.45 weed -logdir=log/master1 master -mdir=master/mdir1 -peers=localhost:9331,localhost:9332,localhost:9333 -port=9331 -defaultReplication=001
  502 13502 13483   0 10:21AM ttys003    0:08.34 weed -logdir=log/master2 master -mdir=master/mdir2 -peers=localhost:9331,localhost:9332,localhost:9333 -port=9332 -defaultReplication=001
  502 13505 13483   0 10:21AM ttys003    0:08.25 weed -logdir=log/master3 master -mdir=master/mdir3 -peers=localhost:9331,localhost:9332,localhost:9333 -port=9333 -defaultReplication=001
  502 13511 13483   0 10:22AM ttys003    0:01.32 weed -logdir=log/volume1 volume -dir=volume/data1 -max=300 -mserver=localhost:9331,localhost:9332,localhost:9333 -port=8081
  502 13514 13483   0 10:22AM ttys003    0:01.20 weed -logdir=log/volume2 volume -dir=volume/data2 -max=300 -mserver=localhost:9331,localhost:9332,localhost:9333 -port=8082
  502 13517 13483   0 10:22AM ttys003    0:01.50 weed -logdir=log/volume3 volume -dir=volume/data3 -max=300 -mserver=localhost:9331,localhost:9332,localhost:9333 -port=8083
  502 13909 13483   0 10:51AM ttys003    0:00.57 weed -logdir=../log/filer1 filer -port=8801 -master=localhost:9331,localhost:9332,localhost:9333 -defaultReplicaPlacement=001
  502 13912 13483   0 10:51AM ttys003    0:00.47 weed -logdir=../log/filer2 filer -port=8802 -master=localhost:9331,localhost:9332,localhost:9333 -defaultReplicaPlacement=001
  502 15984 13483   0 11:34AM ttys003    0:00.00 grep --color=auto weed
bingoo@bogon ~/weed-data>

image image image image

bingoohuang commented 5 years ago

通过filer上传图片测试

curl -v -H "Seaweed-Source: baidu.com" -H "Seaweed-Taging: 小鹿" -F file=@deer.jpg http://localhost:8801/animals/

image

image

image

bingoohuang commented 5 years ago

图片(后缀名为.jpg/.jpeg/.png/.gif)的额外支持

设定宽高:http://localhost:8801/animals/deer.jpg?height=200&width=200 设定宽高,使用模式fit:http://localhost:8801/animals/deer.jpg?height=200&width=200&mode=fit 设定宽高,使用模式fill:http://localhost:8801/animals/deer.jpg?height=200&width=200&mode=fill

区别如下,参考image

Volume Server 参数,自动调整方向

v.fixJpgOrientation = cmdVolume.Flag.Bool("images.fix.orientation", false, "Adjust jpg orientation when uploading.")

bingoohuang commented 5 years ago
bingoo@bogon ~/weed-data> tree .
.
├── filer
│   └── filer.toml
├── log
│   ├── filer1
│   │   ├── weed.ERROR -> weed.bogon.bingoo.log.ERROR.20190108-102346.13521
│   │   ├── weed.FATAL -> weed.bogon.bingoo.log.FATAL.20190108-102346.13521
│   │   ├── weed.INFO -> weed.bogon.bingoo.log.INFO.20190108-105135.13909
│   │   ├── weed.WARNING -> weed.bogon.bingoo.log.WARNING.20190108-102346.13521
│   │   ├── weed.bogon.bingoo.log.ERROR.20190108-102346.13521
│   │   ├── weed.bogon.bingoo.log.FATAL.20190108-102346.13521
│   │   ├── weed.bogon.bingoo.log.INFO.20190108-102346.13521
│   │   ├── weed.bogon.bingoo.log.INFO.20190108-105123.13908
│   │   ├── weed.bogon.bingoo.log.INFO.20190108-105135.13909
│   │   └── weed.bogon.bingoo.log.WARNING.20190108-102346.13521
│   ├── filer2
│   │   ├── weed.INFO -> weed.bogon.bingoo.log.INFO.20190108-105150.13912
│   │   └── weed.bogon.bingoo.log.INFO.20190108-105150.13912
│   ├── master1
│   │   ├── weed.INFO -> weed.bogon.bingoo.log.INFO.20190108-102054.13499
│   │   ├── weed.bogon.bingoo.log.INFO.20190108-101456.13229
│   │   ├── weed.bogon.bingoo.log.INFO.20190108-101533.13286
│   │   └── weed.bogon.bingoo.log.INFO.20190108-102054.13499
│   ├── master2
│   │   ├── weed.INFO -> weed.bogon.bingoo.log.INFO.20190108-102113.13502
│   │   ├── weed.bogon.bingoo.log.INFO.20190108-101609.13323
│   │   └── weed.bogon.bingoo.log.INFO.20190108-102113.13502
│   ├── master3
│   │   ├── weed.INFO -> weed.bogon.bingoo.log.INFO.20190108-102127.13505
│   │   └── weed.bogon.bingoo.log.INFO.20190108-102127.13505
│   ├── volume1
│   │   ├── weed.INFO -> weed.bogon.bingoo.log.INFO.20190108-102204.13511
│   │   ├── weed.bogon.bingoo.log.INFO.20190108-102156.13508
│   │   └── weed.bogon.bingoo.log.INFO.20190108-102204.13511
│   ├── volume2
│   │   ├── weed.INFO -> weed.bogon.bingoo.log.INFO.20190108-102210.13514
│   │   └── weed.bogon.bingoo.log.INFO.20190108-102210.13514
│   └── volume3
│       ├── weed.INFO -> weed.bogon.bingoo.log.INFO.20190108-102215.13517
│       └── weed.bogon.bingoo.log.INFO.20190108-102215.13517
├── master
│   ├── mdir1
│   │   ├── conf
│   │   ├── log
│   │   └── snapshot
│   ├── mdir2
│   │   ├── conf
│   │   ├── log
│   │   └── snapshot
│   └── mdir3
│       ├── conf
│       ├── log
│       └── snapshot
└── volume
    ├── data1
    │   ├── 1.dat
    │   ├── 1.idx
    │   ├── 2.dat
    │   ├── 2.idx
    │   ├── 3.dat
    │   ├── 3.idx
    │   ├── 6.dat
    │   └── 6.idx
    ├── data2
    │   ├── 2.dat
    │   ├── 2.idx
    │   ├── 4.dat
    │   ├── 4.idx
    │   ├── 5.dat
    │   └── 5.idx
    └── data3
        ├── 1.dat
        ├── 1.idx
        ├── 3.dat
        ├── 3.idx
        ├── 4.dat
        ├── 4.idx
        ├── 5.dat
        ├── 5.idx
        ├── 6.dat
        └── 6.idx

21 directories, 59 files
bingoo@bogon ~/weed-data>
bingoohuang commented 5 years ago

Master中的多数据节点维护之 Topology

参考

topology 整个模块最核心的数据结构是三个:

  1. DataCenter 数据中心,跨机房备份用
  2. Rack 机架
  3. DataNode 指Volume Server,管理多个Volume,每个Volume会有对于的磁盘数据文件和索引文件。

topology 是树状结构,DataNode 是树的叶子节点, DataCenter 和 Rack 是树的非叶子节点, DataCenter 是 Rack 的父母节点。 如下图

                             Root
                               |                                                 
                               |
                 ---------------------------------
                |                                 |                    
                |                                 |   
            DataCenter                        DataCenter
                |                                                 
                |
       ------------------
       |                |
       |                |
      Rack            Rack
       |
       |
   ------------
   |          |
   |          |
 DataNode  DataNode
               |
               |
    ------------------
   |                 |
   |                 |
Volume             Volume

一个数据中心包含多个机架。一个机架包含多台机器。在物理上,一台机器包含多个物理卷,在逻辑上,一个逻辑卷包含多个物理卷,逻辑卷是透明的。

bingoohuang commented 5 years ago

数据副本Replication

详见wiki

xyz Meaning
000 只存储一份,默认设置
001 在相同的Rack复制一份
010 在不同的Rack复制一份,相同的数据中心
100 在不同的数据中心复制一份
200 在不同的数据中心复制两份
110 在不同的Rack复制一份,不同的数据中心复制一份
Column Meaning
x number of replica in other data centers
y number of replica in other racks in the same data center
z number of replica in other servers in the same rack

创建的物理副本数量等于X+Y+Z+1,XYZ数字最大为2

bingoohuang commented 5 years ago

Volume的大小

作者在README中所述如下:

In the current implementation, each volume can hold 32 gibibytes (32GiB or 8x2^32 bytes). This is because we align content to 8 bytes. We can easily increase this to 64GiB, or 128GiB, or more, by changing 2 lines of code, at the cost of some wasted padding space due to alignment.

There can be 4 gibibytes (4GiB or 2^32 bytes) of volumes. So the total system size is 8 x 4GiB x 4GiB which is 128 exbibytes (128EiB or 2^67 bytes).

然后master命令行参数中又有设置单volume尺寸默认为30GB≈27.93GiB。

volumeSizeLimitMB = cmdMaster.Flag.Uint("volumeSizeLimitMB", 30*1000, "Master stops directing writes to oversized volumes.")

这里volumeSizeLimitMB实际上是以MiB为单位的,MiB与MB的区别如下

Decimal Metric Binary IEC
1000 kB kilobyte 1024 KiB kibibyte
10002 MB megabyte 10242 MiB mebibyte
10003 GB gigabyte 10243 GiB gibibyte
10004 TB terabyte 10244 TiB tebibyte
10005 PB petabyte 10245 PiB pebibyte
10006 EB exabyte 10246 EiB exbibyte
10007 ZB zettabyte 10247 ZiB zebibyte
10008 YB yottabyte 10248 YiB yobibyte

上传文件的大小

由于当前代码使用DataSize uint32来表示一个文件的大小,实际上也就规定了单个上传文件的最大大小是2^32(4GiB)。见代码:

/*
* A Needle means an uploaded and stored file.
* Needle file size is limited to 4GB for now.
 */
type Needle struct {
    Cookie Cookie   `comment:"random number to mitigate brute force lookups"`
    Id     NeedleId `comment:"needle id"`
    Size   uint32   `comment:"sum of DataSize,Data,NameSize,Name,MimeSize,Mime"`

    DataSize     uint32 `comment:"Data size"` //version2
    Data         []byte `comment:"The actual file data"`
    Flags        byte   `comment:"boolean flags"`          //version2
    NameSize     uint8                                     //version2
    Name         []byte `comment:"maximum 256 characters"` //version2
    MimeSize     uint8                                     //version2
    Mime         []byte `comment:"maximum 256 characters"` //version2
    PairsSize    uint16                                    //version2
    Pairs        []byte `comment:"additional name value pairs, json format, maximum 64kB"`
    LastModified uint64 //only store LastModifiedBytesLength bytes, which is 5 bytes to disk
    Ttl          *TTL

    Checksum   CRC    `comment:"CRC32 to check integrity"`
    AppendAtNs uint64 `comment:"append timestamp in nano seconds"` //version3
    Padding    []byte `comment:"Aligned to 8 bytes"`
}
bingoohuang commented 5 years ago

FileId 组成

image

A file key has 3 parts:

Saving image with different sizes

Each image usually store one file key in database. However, one image can have several versions, e.g., thumbnail, small, medium, large, original. And each version of the same image will have a file key. It's not ideal to store all the keys.

One way to resolve this is here.

Reserve a set of file keys, for example, 5

curl http://<host>:<port>/dir/assign?count=5
{"fid":"3,01637037d6","url":"127.0.0.1:8080","publicUrl":"localhost:8080","count":5}

Save the 5 versions of the image to the volume server. The urls for each image can be:

http://<url>:<port>/3,01637037d6
http://<url>:<port>/3,01637037d6_1
http://<url>:<port>/3,01637037d6_2
http://<url>:<port>/3,01637037d6_3
http://<url>:<port>/3,01637037d6_4
bingoohuang commented 5 years ago

Needle的结构

image

其中,黑色部分是固定大小,灰色部分是动态大小。可见,存盘需要额外33-40bytes(整个Needlek块8byte对齐)。与作者在README中说的额外需要40字节的文件原信息相符。

LeoFS的存储还是很像的: image

bingoohuang commented 5 years ago

grpc 服务

image

bingoohuang commented 5 years ago

文件生存期ttl

申请文件key的时候指定ttl值可以获取指定ttl时间的volume,如果没有匹配的,则新建一个volume,volume级别的ttl会在两种情况下有作用

  1. 指定的ttl volume满了,则新建一个volume
  2. 如果设置为3分钟,3分钟内没有写操作,则这个volume会被停止并删除

curl http://localhost:9333/dir/assign?ttl=3m

在文件上传的时候也可以指定ttl

curl -F “file=@./01.jpg” http://localhost:8081/3,2057c526f7598e?ttl=3m

需要注意的是,应保证文件的ttl小于volume的ttl

TTL值:

bingoohuang commented 5 years ago

FAQ: 在全新空的环境中,向master申请fid时,为啥会创建好多个volume文件,有时候6个,有时候7个?

例如,在curl -v http://localhost:9333/dir/assign请求后,如下生成了6个volume文件。

~/weed-data> tree volume
volume
├── data1
│   ├── 1.dat
│   ├── 1.idx
│   ├── 2.dat
│   ├── 2.idx
│   ├── 3.dat
│   ├── 3.idx
│   ├── 4.dat
│   ├── 4.idx
│   ├── 5.dat
│   ├── 5.idx
│   ├── 6.dat
│   └── 6.idx
├── data10
├── data11
├── data2
│   ├── 1.dat
│   ├── 1.idx
│   ├── 2.dat
│   ├── 2.idx
│   ├── 3.dat
│   ├── 3.idx
│   ├── 4.dat
│   ├── 4.idx
│   ├── 5.dat
│   ├── 5.idx
│   ├── 6.dat
│   └── 6.idx
└── data3

跟踪代码,就可以发现原来是代码中写死如下:

// one replication type may need rp.GetCopyCount() actual volumes
// given copyCount, how many logical volumes to create
func (vg *VolumeGrowth) findVolumeCount(copyCount int) (count int) {
    switch copyCount {
    case 1:
        count = 7
    case 2:
        count = 6
    case 3:
        count = 3
    default:
        count = 1
    }
    return
}

也可以手工通过调用curl http://localhost:9333/vol/grow?count=12&replication=001提升并发写能力

bingoohuang commented 5 years ago

自己编译

for host

mkdir ~/go/src/github.com/chrislusf cd ~/go/src/github.com/chrislusf git clone https://github.com/chrislusf/seaweedfs.git cd seaweedfs go get ./weed/...

然后在~/go/bin下面就可以看到weed二进制程序了,把~/go/bin添加到PATH环境变量中。

for linux

cd weed env GOOS=linux GOARCH=amd64 go build upx weed

使用go install来编译

# in ~/go/src/github.com/chrislusf/seaweedfs
➜  seaweedfs git:(master) ✗ env GOOS=linux GOARCH=amd64 go install ./...
➜  seaweedfs git:(master) ✗ ls ~/go/bin/linux_amd64
bench_filer_upload         compact_leveldb            repeated_vacuum            see_idx                    stress_filer_upload_actual weed
change_superblock          fix_dat                    see_dat                    see_meta                   volume_tailer              weedfuse
➜  seaweedfs git:(master) ✗ cd ~/go/bin/linux_amd64
➜  linux_amd64 upx *
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2018
UPX 3.95        Markus Oberhumer, Laszlo Molnar & John Reiser   Aug 26th 2018

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
   6979309 ->   3642732   52.19%   linux/amd64   bench_filer_upload
  17433916 ->   8899560   51.05%   linux/amd64   change_superblock
   3385090 ->   1777040   52.50%   linux/amd64   compact_leveldb
  17510811 ->   8729304   49.85%   linux/amd64   fix_dat
  17659788 ->   9020144   51.08%   linux/amd64   repeated_vacuum
  19218794 ->   9772072   50.85%   linux/amd64   see_dat
  16412230 ->   8216648   50.06%   linux/amd64   see_idx
  17408286 ->   8867064   50.94%   linux/amd64   see_meta
   6987639 ->   3649988   52.23%   linux/amd64   stress_filer_upload_actual
  17691700 ->   8994500   50.84%   linux/amd64   volume_tailer
  49856701 ->  22511764   45.15%   linux/amd64   weed
  49873636 ->  22527348   45.17%   linux/amd64   weedfuse
   --------------------   ------   -----------   -----------
 240417900 -> 116608164   48.50%                 [ 12 files ]

Packed 12 files.
➜  linux_amd64 cd ..
➜  bin tar -czvf weed.tar.gz linux_amd64
➜  bin /usr/local/bin/sshpass  -p 123123 scp -o StrictHostKeyChecking=no weed.tar.gz root@192.168.126.5:.

单机Debug

server -master.port=9333 -volume.port=8080 -dir="./data" -filer -filer.port=8888

bingoohuang commented 5 years ago

【摘】为什么要用海草

随着业务量增长,一个系统需要存储上百万文件的情况越来越多,尤其是互联网网站。在这种情况下依然使用传统磁盘/共享存储的方式进行支持会有以下问题:

针对以上问题,facebook 提出了自己的方案Facebook’s Haystack design paper 。 之后各种实现出现,如tfs、MogileFS、GlusterFS等,其中Seaweedfs是一个比较优秀的实现。具有效率高、结构简单、代码清晰等优点。

bingoohuang commented 5 years ago

【摘】京东登月平台基础架构技术解析

Glusterfs虽然性能很好,却不适合存储海量小文件,因为它只在宏观上对数据分布作了优化,却没在微观上对文件IO作优化。登月平台上大多数前向服务都是图像识别应用,需要将图片和识别结果保存下来,用作训练数据,进行算法的迭代优化。我们在调研之后采用了SeaweedFS作为小文件存储系统。

SeaweedFS的设计思想源于Facebook的Haystack论文,架构和原理都很简单,性能极好,部署和维护也很方便。

Haystack主要是用于facebook的图片存储的,由于数据量比较多,而且图片访问又有长尾效应,CDN存储,或者一般的Nosql无法满足需求。对于典型的linux文件系统,对于文件的读写,尤其是深目录文件的读写,需要进行多次的磁盘读写,这显然对于系统的响应是有很大影响的。那么减少磁盘读写也就成为haystack的首要目标

SeaweedFS对外提供REST接口,结合它的filer服务可实现目录管理,我们在此基础上实现了批量上传和下载功能。SeaweedFS具有rack-aware和datacenter-aware功能,可根据集群的拓扑结构(节点在机架和数据中心的分布情况)实现更可靠的数据冗余策略。目前登月平台上很多图像服务已经接入SeaweedFS,每天存储的图片数量达到600万张,存储量以每天30G的速度增长。

bingoohuang commented 5 years ago

【摘】分布式存储理论基础CAP,BASE,ACID

CAP

一致性(Consistency),可用性(Availability),分区容忍性(Partitiontolerance)。CAP原理指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。这是Brewer教授于2000年提出的,后人也论证了CAP理论的正确性。

一致性(Consistency)

对于分布式的存储系统,一个数据往往会存在多份。简单的说,一致性会让客户对数据的修改操作(增/删/改),要么在所有的数据副本(replica)全部成功,要么全部失败。即,修改操作对于一份数据的所有副本(整个系统)而言,是原子(atomic)的操作。 如果一个存储系统可以保证一致性,那么则客户读写的数据完全可以保证是最新的。不会发生两个不同的客户端在不同的存储节点中读取到不同副本的情况。

可用性(Availability)

可用性很简单,顾名思义,就是指在客户端想要访问数据的时候,可以得到响应。但是注意,系统可用(Available)并不代表存储系统所有节点提供的数据是一致的。这种情况,我们仍然说系统是可用的。往往我们会对不同的应用设定一个最长响应时间,超过这个响应时间的服务我们仍然称之为不可用的。

分区容忍性(Partition Tolerance) 如果你的存储系统只运行在一个节点上,要么系统整个崩溃,要么全部运行良好。一旦针对同一服务的存储系统分布到了多个节点后,整个存储系统就存在分区的可能性。比如,两个存储节点之间联通的网络断开(无论长时间或者短暂的),就形成了分区。一般来讲,为了提高服务质量,同一份数据放置在不同城市非常正常的。因此节点之间形成分区也很正常。

Gilbert 和Lynch将分区容忍性定义如下:Noset of failures less than total network failure is allowed to cause the systemto respond incorrectly。除全部网络节点全部故障以外,所有子节点集合的故障都不允许导致整个系统不正确响应。另外一篇文章(BASE: An Acid Alternative)中对分区容忍性的解释:Operationswill complete, even if individual components are unavailable。即使部分的组件不可用,施加的操作也可以完成。

BASE

接受最终一致性的理论支撑是BASE模型,BASE全称是BasicallyAvailable(基本可用), Soft-state(软状态/柔性事务), Eventually Consistent(最终一致性)。BASE模型在理论逻辑上是相反于ACID(原子性Atomicity、一致性Consistency、隔离性Isolation、持久性Durability)模型的概念,它牺牲高一致性,获得可用性和分区容忍性。 最终一致性是指:经过一段时间以后,更新的数据会到达系统中的所有相关节点。这段时间就被称之为最终一致性的时间窗口。

ACID

ACID,是指在数据库管理系统(DBMS)中,事务(transaction)所具有的四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。

在数据库系统中,一个事务是指:由一系列数据库操作组成的一个完整的逻辑过程。例如银行转帐,从原账户扣除金额,以及向目标账户添加金额,这两个数据库操作的总和,构成一个完整的逻辑过程,不可拆分。这个过程被称为一个事务,具有ACID特性。

bingoohuang commented 5 years ago

【摘】滴滴对象存储系统GIFT中SeaweedFS的介绍

原文 PPT

滴滴对象存储系统GIFT V1.0

滴滴的对象存储系统,内部称为GIFT,是为了解决公司小文件存储问题而开发的对象存储系统,比如图片、网页静态资源等。 访问的方式为http:////,其中ns即namespace是全局唯一的,ns和key均为用户指定,常见的接口就是上传、删除、下载。一开始这个系统的架构比较简单,主要分两部分,第一部分是API层,用Go实现的接入服务,为用户提供文件上传、下载、查询等功能。后端的存储服务,用于存储文件数据以及元数据。文件数据使用SeaweedFS存储,它是开源的,参考了Facebook的Haystack,不过Haystack本身没有开源。另外在我们一开始设计时,数据和元数据是分离存储的。因为不管是SeaweedFS还是Haystack,要支持大量元数据的存储和复杂的查询,其实是相对困难的。所以元数据单独使用Mysql集群进行存储。

  这里简单介绍一下Facebook Haystack,这是一个比较经典的对象存储系统。Haystack是Facebook用于存储图片等小文件的对象存储系统,其核心思想是采用多对一的映射方式,把多个小文件采用追加写的方式聚合到一个POSIX文件系统上的大文件中,从而大大减少存储系统的元数据,提高文件的访问效率。当要存大量的小文件时,如果用传统的文件系统,一对一地去存小文件,存储空间利用率很低、查询的开销非常高。

  另外介绍一下SeaweedFS的架构。它包含两个组件,一个是数据存储集群,即Volume Server Cluster,每个volume server对应一个磁盘,每个磁盘有一系列的volume文件,每个volume文件默认大小为30GB,用于存储小对象。每个volume文件对应一个index文件,用于记录volume中存储的小对象的偏移和长度,在volume server 启动时缓存在内存中。

  这也是Haystack的一个重要设计思想,它做了一个两级的元数据映射,第一级把它映射到一个大文件,这样文件系统本身的元数据开销是很小的。第二次映射的时候,只需要在大文件内部去找它的偏移和长度。而且做完两级映射之后,可以使大文件里对应的小文件的元数据全部缓存在内存里面,这样就可以大大提高它的查找的效率。

  另外一个组件称为Master Server Cluster,运行的是raft协议,维护集群的一致性状态,一般与volume server混部。volume server会向它上报自己的状态,然后它能够维护整个volume server cluster的拓扑。同时它还负责对象写入时volume server的分配,以及负责数据读取时volume id到volume server地址映射。

  下面我们展开来讲SeaweedFS。首先上传过程,第一步是客户端先与master server cluster的leader通信,leader进行volume分配,给客户端返回一个fskey及volume server地址,fskey包含volume id,needle id及cookie三部分。Volume id跟volume server对应,needle id用于在一个volume内做索引,cookie用于安全考虑,大家有兴趣进一步了解的话,可以参看Haystack的论文。

  第二步,客户端将文件及fskey发送给对应的volume server,该volume server为存储主副本的server,称为primary,primary将文件追加到对应的volume文件尾部,并将offset,length,needle id记录到对应的index文件。

  Primary将文件写入后,将文件发送给两个存储副本的volume server,称为slave,待slave返回写入成功后,给客户端返回写入成功。

  (如下图)

image

删除操作体现了Haystack的思想——它的删除是异步删除的。客户端用对象对应的fskey与master server中的leader通信,得到fskey中volume id对应的volume server地址。这实际上是做了一层间接,因为volume server有可能会挂掉,所以不能直接把volume server的地址给client。同时也便于master做一些负载均衡。每次读的时候也会首先跟leader通信,然后拿到volume server地址之后,再去访问主副本。然后主副本就会把这个对象做一个标记删除,它不会真正去删那个文件,不会真正从大文件里把那部分挖空。只是标记一下,不会做数据删除,Volume server会通过一个异步的compaction操作来回收数据。

  异步compaction是什么?就是说这个大文件,每删一个文件就会留下一个空洞,如果Volume里面已经有很多的空洞了,现在决定要重新用这个资源,就会做一次compaction,就是把Volume里面剩余的、还没有删除的文件全部拷出来,重新把它拼成一个连续的文件,然后把其他的空洞对应的地方释放掉。

  下载其实跟删除类似,首先客户端用对象对应的fskey与master server中的leader通信,得到fskey中volume id对应的volume server地址,然后访问volume server,volume server根据volume id,needle id访问对应volume文件的index文件,得到对象在volume文件内的偏移和长度。Volume server再访问对应volume文件,给client返回对象数据。

伴随业务增长下的第一次演进——GIFT V2.0

  以上介绍就是GIFT最早的版本(V1)。随着本身数据文件量和业务量的增加,问题也越来越多。其中的一个表现是什么?随着业务量不断增加(TB -> PB),架构无法支持PB级存储。因为数据存储SeaweedFS是有中心设计模式,集群规模增大存在并发访问性能瓶颈问题。读写请求必须先经过master server,再访问volume server获取数据。master server运行raft协议,只有一个leader在工作,其它为follower。只有leader处理读写请求,存在单点性能瓶颈。在高并发的情况下,访问延迟明显的增加。然后就是,我们最早的设计,元数据的存储为单库设计,所以是所有用户集群共享的,造成QPS受限。

  所以我们就对它做了一个架构的演进。主要的思想,首先在数据存储方面,我们从业务逻辑角度支持接入服务管理多个SeaweedFS集群,就是多个存储子集群。当一个SeaweedFS集群的容量达到阈值时,将其标记成只读,新数据写入其它集群。而且我们支持SeaweedFS集群的动态加入、动态注册。集群扩容过程为,首先一个新的SeaweedFS集群会向接入服务注册,然后接入服务将其加入可写集群列表,接入服务对上传文件的bucket, object信息做hash,选取一个可写集群,进行写入操作。这是从数据的角度解决QPS和存储容量的需求。

  从元数据的角度,我们支持了支持多库分表模式,可对不同用户集群采用不同库,解决海量文件并发访问问题。下面具体介绍一下。从元数据的角度,我们做了一个分库和分表。首先我们可能有不同的Region,而不同的Region下面可能还有不同的用户集群(下图1)。我们除了有一个全局唯一的配置库以外,从对象的元数据库的角度,做了一个分库和分表的设计。

bingoohuang commented 5 years ago

Collection as a Simple Name Space

Collection as a Simple Name Space When assigning file ids,

curl http://master:9333/dir/assign?collection=pictures curl http://master:9333/dir/assign?collection=documents

will also generate a "pictures" collection and a "documents" collection if they are not created already. Each collection will have its dedicated volumes, and they will not share the same volume.

Actually, the actual data files have the collection name as the prefix, e.g., "pictures_1.dat", "documents_3.dat".

collection的概念

使用collection可以创建一个Volume集合,比如如下命令会创建四个Volume,它会随机分布在不同的文件服务器,在原有Volume ID前边会显示collection集合名称:

curl "http://localhost:9333/vol/grow?collection=test&count=4

重复运行命令会增加count数量的Volume个数

删除collection: curl "http://172.19.12.239:9333/col/delete?collection=test

申请文件key的时候指定collection,上传数据就会上传到collection中: curl "http://172.19.12.239:9333/dir/assign?collection=test这样,对于临临时性定期删除的数据需求,可以使用collection。

Volume结构体

type Volume struct {
    Id            needle.VolumeId
    dir           string
    Collection    string
    dataFile      *os.File
    nm            NeedleMapper
    compactingWg  sync.WaitGroup
    needleMapKind NeedleMapType
    readOnly      bool

    SuperBlock

    dataFileAccessLock    sync.Mutex
    lastModifiedTsSeconds uint64 //unix time in seconds
    lastAppendAtNs        uint64 //unix time in nanoseconds

    lastCompactIndexOffset uint64
    lastCompactRevision    uint16
}
  1. VolumeId 通俗易懂,比如"fid":"3,01f9896771" 里面逗号前面的3 就是VolumeId 。
  2. dir 就是该Volume 所在的目录
  3. Collection 很有用,每个Volume 只能对应同一个Collection,不同Collection 的图片存储在不同Volume,后面会讲到。所以同一个Volume只能针对某一个Collection ,而同一个Collection 的图片可能分布在不同的Volume。dataFile 就是对应的文件句柄。
  4. nm NeedleMapper 看上去像是个map ,其实是个列表,包含多个Needle ,后面会讲到。
  5. readOnly 是否只读
  6. SuperBlock 超级块。
  7. accessLock 互斥锁
  8. lastModifiedTime 最近修改时间

参考

  1. Optimization
  2. SeaweedFs分布式文件存储使用报告
  3. seaweedfs安装、启动
iliul commented 4 years ago

感谢提供了很多PPT素材!

bingoohuang commented 4 years ago

常用命令

Master命令

  1. 分配文件id curl http://localhost:9333/dir/assign
  2. 直接上传 curl -F file=@b.jpg http://localhost:9333/submit
  3. 看volume id分布 curl http://localhost:9333/vol/status
  4. 直接看有哪些volume id curl http://localhost:9333/vol/status |jq '..|objects|select(has("Id"))|.Id'
  5. 看集群状态 http://localhost:9333/cluster/status
  6. 预分配5个volume id curl http://localhost:9333/vol/grow?count=5
  7. Master Web页面 http://127.0.0.1:9333/
  8. volume id分配状态 http://localhost:9333/dir/lookup?volumeId=6

Volume命令

  1. 上传文件 curl -F file=@a1.png http://localhost:8080/5,016a162f85
  2. 查看文件属性 curl -I http://localhost:8080/5,016a162f85
  3. 下载文件 curl -O http://localhost:8080/5,016a162f85
  4. 浏览器直接查看 http://localhost:8080/5,016a162f85
  5. 删除文件 curl -X DELETE http://127.0.0.1:8080/3,01637037d6
  6. volume状态 curl "http://localhost:8080/status?pretty=y"

生成空的volume

curl "http://localhost:9333/vol/grow?count=5" 
# specify a specific replication 
curl "http://localhost:9333/vol/grow?replication=000&count=4" {"count":4} 
# specify a collection 
curl "http://localhost:9333/vol/grow?collection=turbo&count=4" 
# specify data center 
curl "http://localhost:9333/vol/grow?dataCenter=dc1&count=4" 
# specify ttl 
curl "http://localhost:9333/vol/grow?ttl=5d&count=4"

查看volume id的分配情况

$ http "http://localhost:9333/vol/status"
HTTP/1.1 200 OK
Content-Length: 1752
Content-Type: application/json
Date: Thu, 27 Feb 2020 07:34:47 GMT

{
    "Version": "30GB 1.57",
    "Volumes": {
        "DataCenters": {
            "DefaultDataCenter": {
                "DefaultRack": {
                    "127.0.0.1:8081": [
                        {
                            "Collection": "",
                            "CompactRevision": 0,
                        "DeleteCount": 0,
                        "DeletedByteCount": 0,
                        "FileCount": 0,
                        "Id": 1,
                        "ModifiedAtSecond": 1582788549,
                        "ReadOnly": false,
                        "RemoteStorageKey": "",
                        "RemoteStorageName": "",
                        "ReplicaPlacement": {
                            "DiffDataCenterCount": 0,
                            "DiffRackCount": 0,
                            "SameRackCount": 0
                        },
                        "Size": 8,
                        "Ttl": {
                            "Count": 0,
                            "Unit": 0
                        },
                        "Version": 3
                    },
                    {
                        "Collection": "",
                        "CompactRevision": 0,
                        "DeleteCount": 0,
                        "DeletedByteCount": 0,
                        "FileCount": 0,
                        "Id": 2,
                        "ModifiedAtSecond": 1582788549,
                        "ReadOnly": false,
                        "RemoteStorageKey": "",
                        "RemoteStorageName": "",
                        "ReplicaPlacement": {
                            "DiffDataCenterCount": 0,
                            "DiffRackCount": 0,
                            "SameRackCount": 0
                        },
                        "Size": 8,
                        "Ttl": {
                            "Count": 0,
                            "Unit": 0
                        },
                        "Version": 3
                    },
                    {
                        "Collection": "",
                        "CompactRevision": 0,
                        "DeleteCount": 0,
                        "DeletedByteCount": 0,
                        "FileCount": 0,
                        "Id": 5,
                        "ModifiedAtSecond": 1582788549,
                        "ReadOnly": false,
                        "RemoteStorageKey": "",
                        "RemoteStorageName": "",
                        "ReplicaPlacement": {
                            "DiffDataCenterCount": 0,
                            "DiffRackCount": 0,
                            "SameRackCount": 0
                        },
                        "Size": 8,
                        "Ttl": {
                            "Count": 0,
                            "Unit": 0
                        },
                        "Version": 3
                    }
                ],
                "127.0.0.1:8082": [
                    {
                        "Collection": "",
                        "CompactRevision": 0,
                        "DeleteCount": 0,
                        "DeletedByteCount": 0,
                        "FileCount": 0,
                        "Id": 4,
                        "ModifiedAtSecond": 1582788549,
                        "ReadOnly": false,
                        "RemoteStorageKey": "",
                        "RemoteStorageName": "",
                        "ReplicaPlacement": {
                            "DiffDataCenterCount": 0,
                            "DiffRackCount": 0,
                            "SameRackCount": 0
                        },
                        "Size": 8,
                        "Ttl": {
                            "Count": 0,
                            "Unit": 0
                        },
                        "Version": 3
                    }
                ],
                "localhost:8080": [
                    {
                        "Collection": "",
                        "CompactRevision": 0,
                        "DeleteCount": 0,
                        "DeletedByteCount": 0,
                        "FileCount": 0,
                        "Id": 3,
                        "ModifiedAtSecond": 1582788549,
                        "ReadOnly": false,
                        "RemoteStorageKey": "",
                        "RemoteStorageName": "",
                        "ReplicaPlacement": {
                            "DiffDataCenterCount": 0,
                            "DiffRackCount": 0,
                            "SameRackCount": 0
                        },
                        "Size": 8,
                        "Ttl": {
                            "Count": 0,
                            "Unit": 0
                        },
                        "Version": 3
                    }
                ]
            }
        }
    },
    "Free": 202,
    "Max": 207
  }
}

导出命令行

$ weed ./weed help export
Usage: weed export -dir=/tmp -volumeId=234 -o=/dir/name.tar -fileNameFormat={{.Name}} -newer='2006-01-02T15:04:05'
List all files in a volume, or Export all files in a volume to a tar file if the output is specified.
  The format of file name in the tar file can be customized. Default is {{.Mime}}/{{.Id}}:{{.Name}}. Also available is {{.Key}}.
Default Parameters:
-collection string
      the volume collection name
-deleted
      export deleted files. only applies if -o is not specified
-dir string
      input data directory to store volume data files (default ".")
-fileNameFormat string
      filename formatted with {{.Mime}} {{.Id}} {{.Name}} {{.Ext}} (default "{{.Mime}}/{{.Id}}:{{.Name}}")
-limit int
      only show first n entries if specified
-newer string
      export only files newer than this time, default is all files. Must be specified in RFC3339 without timezone, e.g. 2006-01-02T15:04:05
-o string
      output tar file name, must ends with .tar, or just a "-" for stdout
-volumeId int
      a volume id. The volume .dat and .idx files should already exist in the dir. (default -1)
$ weed ./weed export -dir=/tmp/1 -volumeId=6 -o=exportdir/6.tar -limit 1
bingoohuang commented 4 years ago

min.io vs seaweedfs - Object Store showdown

I have tested our two "final" contenders on the Object Store category, ceph went down because of high complexity and high tech knowledge needed to solve problems and achieving stability.

minio advantages:

minio disadvantages:

seaweedfs advantages:

seaweedfs disadvantages:

bingoohuang commented 4 years ago

slideshare seaweedfs-introduction

seaweedfsintroduction.pdf

bingoohuang commented 3 years ago

https://sysadmin.libhunt.com/compare-seaweedfs-vs-ceph

image

bingoohuang commented 3 years ago

SeaweedFS是一个独立的Apache许可的开源项目

SeaweedFS是一个简单且高度可扩展的分布式文件系统。有两个目标:

  1. 存储数十亿个文件!
  2. 快速提供文件!

SeaweedFS最初是一个Object Store,可以有效地处理小文件。中央主服务器不管理中央主服务器中的所有文件元数据,而是仅管理文件卷,并允许这些卷服务器管理文件及其元数据。这减轻了中央主服务器的并发压力,并将文件元数据传播到卷服务器,从而允许更快的文件访问(只需一次磁盘读取操作)。

每个文件的元数据只有40个字节的磁盘存储开销。使用O(1)磁盘读取非常简单,欢迎您使用实际用例挑战性能。

SeaweedFS首先实施了 Facebook的Haystack设计论文

SeaweedFS只能在对象存储中很好地工作。然后可以稍后添加Filer以支持目录和POSIX属性。 Filer是一个单独的线性可伸缩无状态服务器,具有可自定义的元数据存储,例如,MySql / Postgres / Redis / Cassandra / LevelDB。

其他功能

  1. 可以选择不复制或不同的复制级别,机架和数据中心可识别。
  2. 自动主服务器故障转移 - 无单点故障(SPOF)。
  3. 根据文件mime类型自动Gzip压缩。
  4. 自动压缩以在删除或更新后回收磁盘空间。
  5. 同一群集中的服务器可以具有不同的磁盘空间,文件系统,操作系统等。
  6. 添加/删除服务器不会导致任何数据重新平衡。
  7. 可选择固定方向对于jpeg图片。
  8. 支持ETag,Accept-Range,Last-Modified等
  9. 支持内存/ leveldb / boltdb / btree模式调整内存/性能平衡。
  10. 支持重新平衡可写和只读卷。

Filer功能

  1. 文件服务器提供“正常”服务。目录和文件通过http。
  2. mount filer 通过FUSE直接读取和写入文件作为本地目录。
  3. 与Amazon S3兼容的API 以使用S3工具访问文件。
  4. Hadoop兼容文件系统,以便从Hadoop / Spark / Flink /等访问文件作业。
  5. Async Backup To Cloud 具有极快的本地访问权限和备份到Amazon S3,Google Cloud存储,Azure,BackBlaze。
  6. WebDAV 在Mac和Windows或移动设备上作为映射驱动器进行访问。

使用示例

默认情况下,主节点在端口9333上运行,卷节点在端口8080上运行。让我们在端口8080和8081上启动一个主节点和两个卷节点。理想情况下,它们应该从不同的计算机启动。我们将使用localhost作为示例。

SeaweedFS使用HTTP REST操作进行读取,写入和删除。响应采用JSON或JSONP格式。

启动主服务器

> ./weed master

启动卷服务器

  1. > weed volume -dir=”/tmp/data1” -max=5 -mserver=”localhost:9333” -port=8080
  2. > weed volume -dir=”/tmp/data2” -max=10 -mserver=”localhost:9333” -port=8081

写入文件

要上传文件:首先,向/dir/assign发送HTTP POST,PUT或GET请求以获取fid和卷服务器URL:

> curl http://localhost:9333/dir/assign
{“count”:1,”fid”:”3,01637037d6”,”url”:”127.0.0.1:8080”,”publicUrl”:”localhost:8080”}

其次,要存储文件内容,请从响应中将HTTP多部分POST请求发送到url + ‘/‘ + fid:

> curl -F file=@/home/chris/myphoto.jpg
http://127.0.0.1:8080/3,01637037d6{“name”:”myphoto.jpg”,”size”:43234,”eTag”:”1cc0118e”}

要进行更新,请发送包含更新文件内容的其他POST请求。

要删除,请将HTTP DELETE请求发送到同一个url + ‘/‘ + fid网址:

> curl -X DELETE http://127.0.0.1:8080/3,01637037d6

保存文件ID

现在,您可以将fid,3,01637037d6保存到数据库字段中。

开头的数字3表示卷ID。在逗号之后,它是一个文件密钥01和一个文件cookie,637037d6。

volume id是无符号的32位整数。文件密钥是无符号的64位整数。文件cookie是无符号的32位整数,用于防止URL猜测。

文件密钥和文件cookie都以十六进制编码。您可以存储&lt;卷ID,文件密钥,文件cookie&gt;您自己格式的元组,或者只是将fid存储为字符串。

如果存储为字符串,理论上,您需要8 + 1 + 16 + 8 = 33个字节。如果不是绰绰有余,char(33)就足够了,因为大多数用途不需要2 ^ 32卷。

如果空间确实存在问题,您可以使用自己的格式存储文件ID。对于卷id,您需要一个4字节的整数,对于文件密钥,需要一个8字节的长整数,对于文件cookie,需要一个4字节的整数。所以16个字节绰绰有余。

读取文件

以下是如何呈现网址的示例。

首先按文件的volumeId查找卷服务器的URL:

`> curl http://localhost:9333/dir/lookup?volumeId=3
{“volumeId”:”3”,”locations”:[{“publicUrl”:”localhost:8080”,”url”:”localhost:8080”}]}

由于(通常)卷服务器不是太多,并且卷不经常移动,因此您可以在大多数时间缓存结果。根据复制类型,一个卷可以具有多个副本位置。只需随机选择一个位置即可阅读。

现在您可以通过网址获取公共网址,渲染网址或直接从卷服务器读取:

http://localhost:8080/3,01637037d6.jpg

请注意,我们添加了一个文件扩展名“.jpg”这里。它是可选的,只是客户端指定文件内容类型的一种方式。

如果您想要更好的网址,可以使用以下其中一种替代网址格式:

  1. http://localhost:8080/3/01637037d6/my_preferred_name.jpg
  2. http://localhost:8080/3/01637037d6.jpg
  3. http://localhost:8080/3,01637037d6.jpg
  4. http://localhost:8080/3/01637037d6
  5. http://localhost:8080/3,01637037d6

如果您想获得图像的缩放版本,可以添加一些参数:

  1. http://localhost:8080/3/01637037d6.jpg?height=200&width=200
  2. http://localhost:8080/3/01637037d6.jpg?height=200&width=200&mode=fit
  3. http://localhost:8080/3/01637037d6.jpg?height=200&width=200&mode=fill

机架感知和数据中心感知复制

SeaweedFS在卷级别应用复制策略。因此,当您获得文件ID时,您可以指定复制策略。例如:

curl http://localhost:9333/dir/assign?replication=001

复制参数选项为:

  1. 000: no replication
  2. 001: replicate once on the same rack
  3. 010: replicate once on a different rack, but same data center
  4. 100: replicate once on a different data center
  5. 200: replicate twice on two different data center
  6. 110: replicate once on a different rack, and once on a different data center

有关复制的更多详细信息,请参阅维基上的 。您还可以在启动主服务器时设置默认复制策略。

在特定数据中心分配文件密钥

可以使用特定数据中心名称启动卷服务器:

weed volume -dir=/tmp/1 -port=8080 -dataCenter=dc1weed volume -dir=/tmp/2 -port=8081 -dataCenter=dc2

在请求文件密钥时,可选的“数据中心”参数可以将指定的音量限制为特定数据中心。例如,这指定分配的卷应限制为“dc1”:

http://localhost:9333/dir/assign?dataCenter=dc1

其他功能

  1. 无单点故障
  2. 使用您自己的密钥插入
  3. 分块大文件
  4. 集合为简单名称空间

架构

通常,分布式文件系统将每个文件分成块,中央主机保存文件名,块索引到块句柄的映射,以及每个块服务器具有的块。

主要缺点是中央主机无法有效处理许多小文件,并且由于所有读取请求都需要通过块主机,因此对于许多并发用户来说可能无法很好地扩展。

SeaweedFS管理主服务器中的数据卷,而不是管理块。每个数据卷大小为32GB,可以容纳大量文件。每个存储节点可以有许多数据量。因此主节点只需要存储有关卷的元数据,这是一个相当少量的数据,并且通常是稳定的。

实际文件元数据存储在卷服务器上的每个卷中。由于每个卷服务器仅管理其自身磁盘上的文件元数据,每个文件只有16个字节,因此所有文件访问只能从内存中读取文件元数据,只需要一个磁盘操作即可实际读取文件数据。

为了进行比较,请考虑Linux中的xfs inode结构为536字节。

主服务器和卷服务器

架构非常简单。实际数据存储在存储节点上的卷中。一个卷服务器可以有多个卷,并且都可以通过基本身份验证支持读写访问。

所有卷都由主服务器管理。主服务器包含卷ID到卷服务器映射。这是相当静态的信息,可以轻松缓存。

在每个写请求中,主服务器还生成一个文件密钥,这是一个不断增长的64位无符号整数。由于写请求通常不像读请求那样频繁,因此一个主服务器应该能够很好地处理并发。

写入和读取文件

当客户端发送写入请求时,主服务器返回该文件的(卷标识,文件密钥,文件cookie,卷节点URL)。然后,客户端联系卷节点并POST文件内容。

当客户端需要根据(卷标识,文件密钥,文件cookie)读取文件时,它会根据(卷节点URL,卷节点公共URL)的卷标识向主服务器,或者从缓存。然后客户端可以获取内容,或者只是在网页上呈现URL并让浏览器获取内容。

有关写入读取过程的详细信息,请参阅示例。

存储空间

在当前实现中,每个卷可以容纳32个gibibytes(32GiB或8x2 ^ 32个字节)。这是因为我们将内容与8个字节对齐。我们可以通过更改2行代码轻松地将其增加到64GiB或128GiB或更多,代价是由于对齐而浪费了一些填充空间。

可以有4个gibibytes(4GiB或2 ^ 32字节)的卷。因此总系统大小为8 x 4GiB x 4GiB,即128 exbibytes(128EiB或2 ^ 67字节)。

每个单独的文件大小都限制为卷大小。

保存内存

存储在卷服务器上的所有文件元信息都可以从内存中读取而无需磁盘访问。每个文件只需要一个16字节的映射条目,其中包括 64bit密钥,32位偏移,32位大小。当然,每个地图条目都有自己的地图空间成本。但通常磁盘空间会在内存耗尽之前耗尽。

与其他文件系统相比

大多数其他分布式文件系统似乎比必要的更复杂。

SeaweedFS在设置和操作方面都要快速而简单。如果你到达这里时不明白它是如何工作的,我们就失败了!如有任何问题,请提出问题或通过说明更新此文件。

与HDFS相比

HDFS对每个文件使用块方法,非常适合存储大文件。

SeaweedFS非常适合快速并同时提供相对较小的文件。

SeaweedFS还可以通过将其分成可管理的数据块来存储超大文件,并将数据块的文件ID存储到元块中。这是由“海草”管理的上传/下载工具,海草主服务器或卷服务器对它不了解。

与GlusterFS相比,Ceph

架构大致相同。 SeaweedFS旨在通过简单而扁平的架构快速存储和读取文件。主要区别是

  1. SeaweedFS优化小文件,确保O(1)磁盘搜索操作,并且还可以处理大文件。 1.SeaweedFS静态分配文件的卷ID。查找文件内容只是查找卷ID,可以轻松缓存。
  2. SeaweedFS Filer元数据存储可以是任何众所周知且经过验证的数据存储,例如Cassandra,Redis,MySql,Postgres等,并且易于定制。
  3. SeaweedFS Volume服务器还通过HTTP直接与客户通信,支持范围查询,直接上传等。
系统 文件元 文件内容读 POSIX REST API 针对小文件进行了优化
SeaweedFS 查询卷ID,可缓存 O(1) 磁盘搜索
SeaweedFS Filer 线性可扩展,可自定义 O(1) 磁盘搜索 FUSE
GlusterFS 哈希 FUSE NFS
Ceph 散列+规则 FUSE

与GlusterFS相比

GlusterFS将文件(包括目录和内容)存储在名为“砖块”的可配置卷中。

GlusterFS将路径和文件名散列为ids,并分配给虚拟卷,然后映射到“砖块”。

与Ceph相比

Ceph可以像SeaweedFS一样设置为key-blob存储。它要复杂得多,需要在它上面支持层。 这是一个更详细的比较

SeaweedFS有一个集中的主组来查找免费卷,而Ceph使用散列和元数据服务器来查找其对象。拥有一个集中的主人可以轻松编码和管理。

与SeaweedFS相同,Ceph也基于RADOS对象存储。 Ceph相当复杂,评论不一。

Ceph使用CRUSH散列来自动管理数据放置。 SeaweedFS按指定的数量放置数据。

SeaweedFS针对小文件进行了优化。小文件存储为一个连续的内容块,文件之间最多有8个未使用的字节。小文件访问是O(1)磁盘读取。

SeaweedFS Filer使用现成的存储,如MySql,Postgres,Redis,Cassandra来管理文件目录。经过验证,可扩展且易于管理。

SeaweedFS 与Ceph

主 MDS 简单 为小文件优化的 卷 OSD Filer Ceph FS 可线性扩展,可自定义,O(1)或O(logN)

磁盘相关主题

硬盘性能

测试读取性能时SeaweedFS,它基本上成为您硬盘随机读取速度的性能测试。硬盘通常达到100MB / s~200MB / s。

固态硬盘

要修改或删除小文件,SSD必须一次删除整个块,并将现有块中的内容移动到新块。 SSD在全新时速度很快,但随着时间的推移会变得支离破碎,你必须进行垃圾收集,压缩块。 SeaweedFS对SSD很友好,因为它只附加。删除和压缩是在后台的卷级别上完成的,不会减慢读取速度而不会导致碎片化。

基准

我自己的不科学单机结果在Macbook上使用固态硬盘,CPU:1英特尔酷睿i7 2.6GHz。

写一百万个1KB文件:

Concurrency Level:      16
Time taken for tests:   88.796 seconds
Complete requests:      1048576
Failed requests:        0
Total transferred:      1106764659 bytes
Requests per second:    11808.87 [#/sec]
Transfer rate:          12172.05 [Kbytes/sec]
Connection Times (ms)   min      avg        max      std
Total:                  0.2      1.3       44.8      0.9
Percentage of the requests served within a certain time (ms)   
50%      1.1 ms   
66%      1.3 ms   
75%      1.5 ms   
80%      1.7 ms   
90%      2.1 ms   
95%      2.6 ms   
98%      3.7 ms 

随机阅读100万个文件:

Concurrency Level:      16
Time taken for tests:   34.263 seconds
Complete requests:      1048576
Failed requests:        0
Total transferred:      1106762945 bytes
Requests per second:    30603.34 [#/sec]
Transfer rate:          31544.49 [Kbytes/sec]

Connection Times (ms)              min      avg        max      std
Total:        0.0      0.5       20.7      0.7
Percentage of the requests served within a certain time (ms)   
50%      0.4 ms   75%      0.5 ms   95%      0.6 ms   
98%      0.8 ms   99%      1.2 ms  100%     20.7 ms

许可证

根据Apache许可证2.0版(“许可证”)获得许可;除非符合许可,否则您不得使用此文件。您可以在

获取许可证副本 http://www.apache.org/licenses/LICENSE-2.0

除非适用法律要求或书面同意,否则根据许可证分发的软件将按“原样”分发。基础,不附带任何明示或暗示的担保或条件。有关管理许可下的权限和限制的特定语言,请参阅许可证。

来源 http://www.xmanong.com/project/18081

bingoohuang commented 3 years ago

蚁阅海量文章存储方案 http://blog.guyskk.com/notes/rssant-story-storage

蚁阅 是一个 RSS 阅读服务, 全文阅读是其中一个很实用的功能,可以为读者提供流畅和一致的阅读体验。

早期在存储方面没有考虑太多,直接把文章信息和全文存储在 PostgreSQL 里面,随着订阅量越来越多, 这个表也越来越大(每个订阅至多保留 1K 篇文章),不久的将来会达到几千万这个量级。这会带来两个问题:

  1. 单表/单数据库的处理能力受限于单块磁盘的 IO 性能,会成为瓶颈。
  2. 数据备份和恢复会越来越慢。

要解决 #1,需要对数据做分片,使用多个磁盘。要解决 #2,需要支持增量备份和恢复。

使用云厂商的对象存储方案,可以很好的解决这两个问题。 但是蚁阅有点特殊,首先我不希望绑定到任何云厂商,说不定哪天就被封了,这完全不可控; 其次蚁阅要支持独立部署,哪怕蚁阅服务没了,大家也能自己部署继续用这个产品。

不能使用云厂商的方案,那就找开源替代方案,调研下来大致有这些方案:

  1. Ceph

  2. MinIO

  3. SeaweedFS

  4. Ceph 是重量型方案,对小项目来说太复杂了。

  5. MinIO 比较新,部署相对比较简单,但架构和配置还是挺复杂的。

  6. SeaweedFS 部署简单,架构也简单,基于 Facebook Haystack 设计。

SeaweedFS方案

我首先按 SeaweedFS 设计了一版存储方案。SeaweedFS 相当于 KV 存储,键称为 fid。 例如: 3,01637037d6,开头的 3 表示 Volume ID,中间的 01 是文件的 Key,用于定位文件, 后 8 位是 Cookie,用于防止猜解 URL,这里似乎用不上。Key 和 Cookie 都是 32 位整数,用 16 进制表示。

SeaweedFS 有 Master 节点和 Volume 节点,Master 负责管理 Volume ID 到 Volume 节点的映射关系,Volume 节点负责存储数据。 通常可以由 Master 分配 fid,保证全局唯一和数据平衡。也支持自定义生成规则。

实际上每个 Volume 存储为磁盘上一个文件,SeaweedFS 会在内存里维护 fid 到文件读写位置的映射, 实现每次查询和写入都只需要 1 次 IO 操作,同一个 Volume 的操作是串行的。

蚁阅使用自定义 fid 生成规则,由 feed_id 和 offset 唯一确定一篇文章,不依赖于 Master 节点:

story key: 64 bits
+----------+---------+--------+----------+
|     4    |   28    |   28   |    4     |
+----------+---------+--------+----------+
| reserve1 | feed_id | offset | reserve2 |
+----------+---------+-------------------+

reserve1 和 reserve2 是保留位,目前没有用到。

然后数据按 feed_id 范围分片,比如每 1K 个订阅的文章存储在一个 Volume,每个分片数据大约是 1G, SeaweedFS 每个 Volume 最大支持 32G。 范围分片很容易支持扩容,随着订阅量增长只需添加更多的 Volume 节点,不用数据迁移。

但范围分片可能会有数据不均匀,所以稍微改进一下,每 8 个 Volume 一组, 数据按 feed_id 范围分组,每 8K 个订阅一组,组内再哈希分片。 这样数据分布会比较均匀,扩容时就一次扩容一组 Volume。

性能分析

按这个方案实现之后,做了一些性能分析。

首先在阿里云 200G 高效云盘上,磁盘性能大约为 3000 IOPS。 使用 SeaweedFS 自带的 benchmark 命令,可以测到写入 QPS 接近 3000, 符合每次查询和写入都只需要 1 次 IO 操作,接近磁盘性能极限。

Concurrency Level:      16
Time taken for tests:   384.116 seconds
Complete requests:      1048576
Failed requests:        0
Total transferred:      1106812710 bytes
Requests per second:    2729.84 [#/sec]
Transfer rate:          2813.92 [Kbytes/sec]

Connection Times (ms)
              min      avg        max      std
Total:        0.5      5.8       107.9      3.5

Percentage of the requests served within a certain time (ms)
   50%      4.8 ms
   66%      6.4 ms
   75%      7.2 ms
   80%      7.9 ms
   90%     10.0 ms
   95%     12.4 ms
   98%     15.5 ms
   99%     18.2 ms
  100%    107.9 ms

但是蚁阅更新订阅时是批量写入文章,我发现这个操作很慢。 如果一个订阅有 50 篇文章,全部写入就需要 500 ms,而且并行发请求效果和串行耗时一样。

所以问题可能是 Volume 数量影响了并行性能,我用 aiohttp 写了个脚本测试不同的 Volume 数量和客户端并发数对性能的影响。(详细结果附在文末)

结论是:增大并发数或 Volume 数对性能提升非常小,性能取决于磁盘,而单个磁盘 IO 是串行的。

PostgreSQL方案

从监控数据发现,SeaweedFS 方案比改写前慢了 3 倍左右。 猜想是由于蚁阅都是批量写入,而且数据都是连续的,PostgreSQL 会把一个事务里的 IO 操作合并,提升了性能。

因此我又设计了一版基于 PostgreSQL 的存储方案,表结构如下:

CREATE TABLE IF NOT EXISTS story_volume_0 (
    id BIGINT PRIMARY KEY,
    content BYTEA NOT NULL
)

主键 ID 按上文 story key 方式编码。一开始我尝试 feed_id, offset 联合主键,发现 Django 不支持, 遂改用自定义编码,效果是一样的,同时支持范围查找和范围删除。

数据分片同样用范围分片,只是分片大小设为了 64K,单表大约 1KW 行数据。 配置里指定 Volume ID 到 “数据库 + 表” 的映射关系,表会在首次使用时创建。 扩容只需部署好 PostgreSQL,然后增加映射关系即可。

写入操作,支持批量写入:

INSERT INTO story_volume_0 (id, content) VALUES (:id, :content)
ON CONFLICT (id) DO UPDATE SET content = EXCLUDED.content

我用 asyncpg 简单实现了一版,然后做了个对比测试,单个写性能略好于 SeaweedFS, 但延迟略高于 SeaweedFS。(详细结果附在文末)

正式的实现用的是 SQLAlchemy + psycopg2,有连接池性能很好,实现起来也比较稳妥。 从监控数据上看,性能和改写前基本持平,所以没有再对比批量写的性能了。

综合考虑,PostgreSQL 迁移成本较低,不用多维护一个组件,性能也不错,就选定它了。

数据压缩

文本数据压缩是很有必要的,能节省很多存储空间。数据库已经内置支持压缩,但我还是在应用层做了压缩,原因是: 数据库是有状态服务,迁移和扩容比较麻烦,而应用层是无状态的,扩容非常方便,所以尽量把负载转移到无状态服务上。

压缩算法有非常多,要在压缩率,压缩速度,解压速度之间权衡,这个网站提供了很有用的压测数据: http://quixdb.github.io/squash-benchmark/

在压缩数据上我增加了 1 字节的自定义版本号,可以方便支持不同的压缩算法。

+----------+-------------+
|  1 byte  |   N bytes   |
+----------+-------------+
|  version |   content   |
+----------+-------------+

目前对小于 1KB 的文章,不需要压缩,小于 16KB 的文章(大部分),用 lz4 压缩比较快, 大于 16KB 的文章,用 gzip 压缩率比较高,节省存储空间。

数据兼容和升级

这次存储改造后兼容上一版(1.4.2)的数据,只是增加了几个新表,无需做数据迁移,可以平滑升级。 应用层会先读新表数据,读不到再去读旧表,性能会有一点损失,但保持数据兼容是更明智的做法, 可以避免很多不必要的风险和痛苦。

另外 Django 的充血模型,不适应复杂的存储架构,后续要慢慢重构分层。

数据冗余和容灾

目前蚁阅只有一个数据库,依靠每天全量备份做容灾,后续要考虑增量备份。

自己做多副本容灾的话难度比较大,投入产出比太低,所以后续会考虑混合云方案, 即依靠云厂商抵抗自然灾害,靠自己活下去,哪怕网线被拔了也要能快速复活。

附存储方案压测结果

测试环境为 Mac Docker,磁盘 IO 比较差。结果仅供参考。 每个测试跑 3 轮,第一行为写性能,第二行为读性能。

SeaweedFS方案

1 volume, 1 concurrency
0 ------------------------------------------------------------
avg=2.7 p50=2.6 p80=2.8 p90=3.0 p95=3.6 p99=5.9 qps=373
avg=2.4 p50=2.3 p80=2.6 p90=2.9 p95=3.2 p99=4.6 qps=415
1 ------------------------------------------------------------
avg=2.7 p50=2.6 p80=2.9 p90=3.2 p95=3.7 p99=5.7 qps=368
avg=2.4 p50=2.4 p80=2.6 p90=2.8 p95=3.1 p99=4.5 qps=409
2 ------------------------------------------------------------
avg=2.6 p50=2.5 p80=2.8 p90=3.0 p95=3.3 p99=5.9 qps=383
avg=2.3 p50=2.2 p80=2.4 p90=2.6 p95=2.8 p99=3.2 qps=444
2 volume, 2 concurrency
0 ------------------------------------------------------------
avg=5.3 p50=3.4 p80=4.6 p90=6.0 p95=22.8 p99=40.7 qps=381
avg=4.3 p50=2.9 p80=3.7 p90=4.5 p95=6.0 p99=37.3 qps=470
1 ------------------------------------------------------------
avg=5.1 p50=3.4 p80=4.5 p90=5.7 p95=14.0 p99=38.1 qps=393
avg=4.4 p50=2.9 p80=3.7 p90=4.5 p95=6.3 p99=36.7 qps=459
2 ------------------------------------------------------------
avg=5.3 p50=3.5 p80=4.6 p90=5.7 p95=25.2 p99=41.4 qps=378
avg=4.2 p50=2.9 p80=3.8 p90=4.8 p95=6.4 p99=37.0 qps=475
2 volume, 20 concurrency
0 ------------------------------------------------------------
avg=55.2 p50=66.5 p80=86.4 p90=92.1 p95=97.3 p99=109.5 qps=363
avg=44.9 p50=26.5 p80=77.5 p90=84.6 p95=89.5 p99=96.3 qps=445
1 ------------------------------------------------------------
avg=57.4 p50=71.7 p80=87.9 p90=94.1 p95=98.4 p99=109.9 qps=349
avg=45.7 p50=27.6 p80=76.7 p90=84.3 p95=90.4 p99=101.4 qps=438
2 ------------------------------------------------------------
avg=60.3 p50=74.0 p80=90.9 p90=96.9 p95=101.9 p99=111.1 qps=332
avg=43.9 p50=23.5 p80=76.9 p90=83.8 p95=88.6 p99=96.0 qps=456
10 volume, 20 concurrency
0 ------------------------------------------------------------
avg=60.3 p50=75.4 p80=90.3 p90=95.2 p95=100.6 p99=107.3 qps=332
avg=53.9 p50=62.8 p80=86.2 p90=92.4 p95=97.6 p99=104.4 qps=371
1 ------------------------------------------------------------
avg=58.1 p50=71.2 p80=88.7 p90=95.2 p95=98.7 p99=105.7 qps=344
avg=47.2 p50=25.8 p80=80.1 p90=86.7 p95=91.0 p99=100.1 qps=424
2 ------------------------------------------------------------
avg=59.0 p50=68.7 p80=89.0 p90=95.2 p95=101.0 p99=107.9 qps=339
avg=49.3 p50=44.1 p80=81.9 p90=88.3 p95=94.9 p99=100.8 qps=406

PostgreSQL方案

concurrency=1
0 ------------------------------------------------------------
avg=3.6 p50=3.5 p80=3.9 p90=4.2 p95=4.6 p99=6.4 qps=275
avg=3.2 p50=3.1 p80=3.5 p90=3.8 p95=4.2 p99=5.0 qps=310
1 ------------------------------------------------------------
avg=3.9 p50=3.8 p80=4.2 p90=4.6 p95=4.9 p99=6.4 qps=254
avg=3.1 p50=2.9 p80=3.3 p90=3.7 p95=4.0 p99=7.9 qps=322
2 ------------------------------------------------------------
avg=4.2 p50=3.9 p80=4.5 p90=4.9 p95=5.4 p99=8.5 qps=240
avg=3.2 p50=3.1 p80=3.5 p90=3.8 p95=4.1 p99=4.8 qps=310
concurrency=2
0 ------------------------------------------------------------
avg=4.7 p50=4.5 p80=5.2 p90=5.8 p95=6.3 p99=8.2 qps=426
avg=3.6 p50=3.5 p80=4.0 p90=4.4 p95=4.7 p99=6.5 qps=556
1 ------------------------------------------------------------
avg=4.5 p50=4.4 p80=4.9 p90=5.4 p95=5.8 p99=7.1 qps=443
avg=3.7 p50=3.5 p80=4.0 p90=4.4 p95=4.7 p99=7.3 qps=547
2 ------------------------------------------------------------
avg=4.7 p50=4.5 p80=5.1 p90=5.6 p95=6.2 p99=7.7 qps=426
avg=3.8 p50=3.6 p80=4.2 p90=4.7 p95=5.3 p99=6.3 qps=529
concurrency=5
0 ------------------------------------------------------------
avg=10.5 p50=7.3 p80=10.1 p90=19.2 p95=38.2 p99=50.1 qps=474
avg=5.9 p50=5.4 p80=6.5 p90=7.4 p95=9.7 p99=15.8 qps=853
1 ------------------------------------------------------------
avg=10.7 p50=7.4 p80=10.1 p90=28.8 p95=38.5 p99=45.0 qps=466
avg=6.2 p50=5.6 p80=6.8 p90=7.9 p95=9.2 p99=20.7 qps=813
2 ------------------------------------------------------------
avg=10.5 p50=7.8 p80=10.7 p90=21.1 p95=33.8 p99=38.5 qps=475
avg=6.5 p50=6.3 p80=7.6 p90=8.5 p95=9.3 p99=12.3 qps=768
concurrency=10
0 ------------------------------------------------------------
avg=21.7 p50=15.7 p80=37.6 p90=47.1 p95=52.2 p99=60.7 qps=461
avg=14.7 p50=12.7 p80=18.2 p90=26.0 p95=35.7 p99=41.6 qps=680
1 ------------------------------------------------------------
avg=22.4 p50=16.0 p80=40.3 p90=52.0 p95=56.1 p99=62.7 qps=447
avg=13.3 p50=11.8 p80=16.4 p90=20.7 p95=27.1 p99=41.1 qps=754
2 ------------------------------------------------------------
avg=24.0 p50=16.9 p80=44.4 p90=53.6 p95=58.2 p99=79.3 qps=416
avg=15.0 p50=12.8 p80=17.6 p90=27.2 p95=34.0 p99=44.6 qps=668
WyattJia commented 2 years ago

大佬,seaweedfs 两地三中心同步方案有参考建议吗?