-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
海草海草随波飘摇,海草海草浪花里舞蹈 #57
Comments
master命令行参数帮助,可以通过 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") |
volume命令行参数帮助,可以通过 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.") |
filer命令行参数帮助,可以通过 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") |
启动一个测试集群:2 filer(8801-8802) + 3 master(9331-9333) + 3 volume(8081-8083)
filer默认在./filerdb中。
注意logdir参数的位置,是紧接着在weed之后。
|
通过filer上传图片测试
|
图片(后缀名为.jpg/.jpeg/.png/.gif)的额外支持设定宽高:http://localhost:8801/animals/deer.jpg?height=200&width=200 区别如下,参考: Volume Server 参数,自动调整方向v.fixJpgOrientation = cmdVolume.Flag.Bool("images.fix.orientation", false, "Adjust jpg orientation when uploading.") |
|
Master中的多数据节点维护之 Topologytopology 整个模块最核心的数据结构是三个:
topology 是树状结构,DataNode 是树的叶子节点, DataCenter 和 Rack 是树的非叶子节点, DataCenter 是 Rack 的父母节点。 如下图
一个数据中心包含多个机架。一个机架包含多台机器。在物理上,一台机器包含多个物理卷,在逻辑上,一个逻辑卷包含多个物理卷,逻辑卷是透明的。 |
数据副本Replication
创建的物理副本数量等于X+Y+Z+1,XYZ数字最大为2 |
Volume的大小作者在README中所述如下:
然后master命令行参数中又有设置单volume尺寸默认为30GB≈27.93GiB。 volumeSizeLimitMB = cmdMaster.Flag.Uint("volumeSizeLimitMB", 30*1000, "Master stops directing writes to oversized volumes.") 这里volumeSizeLimitMB实际上是以MiB为单位的,MiB与MB的区别如下:
上传文件的大小由于当前代码使用 /*
* 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"`
} |
FileId 组成A file key has 3 parts:
Saving image with different sizesEach 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 |
Needle的结构其中,黑色部分是固定大小,灰色部分是动态大小。可见,存盘需要额外33-40bytes(整个Needlek块8byte对齐)。与作者在README中说的额外需要40字节的文件原信息相符。 跟LeoFS的存储还是很像的: |
文件生存期ttl申请文件key的时候指定ttl值可以获取指定ttl时间的volume,如果没有匹配的,则新建一个volume,volume级别的ttl会在两种情况下有作用
在文件上传的时候也可以指定ttl curl -F “file=@./01.jpg” http://localhost:8081/3,2057c526f7598e?ttl=3m 需要注意的是,应保证文件的ttl小于volume的ttl TTL值:
|
FAQ: 在全新空的环境中,向master申请fid时,为啥会创建好多个volume文件,有时候6个,有时候7个?例如,在
跟踪代码,就可以发现原来是代码中写死如下: // 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
} 也可以手工通过调用 |
自己编译for host
然后在~/go/bin下面就可以看到weed二进制程序了,把~/go/bin添加到PATH环境变量中。 for linux
使用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 [email protected]:. 单机Debugserver -master.port=9333 -volume.port=8080 -dir="./data" -filer -filer.port=8888 |
【摘】为什么要用海草随着业务量增长,一个系统需要存储上百万文件的情况越来越多,尤其是互联网网站。在这种情况下依然使用传统磁盘/共享存储的方式进行支持会有以下问题:
针对以上问题,facebook 提出了自己的方案Facebook’s Haystack design paper 。 之后各种实现出现,如tfs、MogileFS、GlusterFS等,其中Seaweedfs是一个比较优秀的实现。具有效率高、结构简单、代码清晰等优点。 |
【摘】京东登月平台基础架构技术解析Glusterfs虽然性能很好,却不适合存储海量小文件,因为它只在宏观上对数据分布作了优化,却没在微观上对文件IO作优化。登月平台上大多数前向服务都是图像识别应用,需要将图片和识别结果保存下来,用作训练数据,进行算法的迭代优化。我们在调研之后采用了SeaweedFS作为小文件存储系统。 SeaweedFS的设计思想源于Facebook的Haystack论文,架构和原理都很简单,性能极好,部署和维护也很方便。 Haystack主要是用于facebook的图片存储的,由于数据量比较多,而且图片访问又有长尾效应,CDN存储,或者一般的Nosql无法满足需求。对于典型的linux文件系统,对于文件的读写,尤其是深目录文件的读写,需要进行多次的磁盘读写,这显然对于系统的响应是有很大影响的。那么减少磁盘读写也就成为haystack的首要目标 SeaweedFS对外提供REST接口,结合它的filer服务可实现目录管理,我们在此基础上实现了批量上传和下载功能。SeaweedFS具有rack-aware和datacenter-aware功能,可根据集群的拓扑结构(节点在机架和数据中心的分布情况)实现更可靠的数据冗余策略。目前登月平台上很多图像服务已经接入SeaweedFS,每天存储的图片数量达到600万张,存储量以每天30G的速度增长。 |
【摘】分布式存储理论基础CAP,BASE,ACIDCAP一致性(Consistency),可用性(Availability),分区容忍性(Partitiontolerance)。CAP原理指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。这是Brewer教授于2000年提出的,后人也论证了CAP理论的正确性。
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)模型的概念,它牺牲高一致性,获得可用性和分区容忍性。 最终一致性是指:经过一段时间以后,更新的数据会到达系统中的所有相关节点。这段时间就被称之为最终一致性的时间窗口。 ACIDACID,是指在数据库管理系统(DBMS)中,事务(transaction)所具有的四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。 在数据库系统中,一个事务是指:由一系列数据库操作组成的一个完整的逻辑过程。例如银行转帐,从原账户扣除金额,以及向目标账户添加金额,这两个数据库操作的总和,构成一个完整的逻辑过程,不可拆分。这个过程被称为一个事务,具有ACID特性。
|
【摘】滴滴对象存储系统GIFT中SeaweedFS的介绍滴滴对象存储系统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返回写入成功后,给客户端返回写入成功。 (如下图) 删除操作体现了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)。我们除了有一个全局唯一的配置库以外,从对象的元数据库的角度,做了一个分库和分表的设计。 |
Collection as a Simple Name SpaceCollection as a Simple Name Space When assigning file ids,
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集合名称:
重复运行命令会增加count数量的Volume个数 删除collection: 申请文件key的时候指定collection,上传数据就会上传到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
}
参考 |
感谢提供了很多PPT素材! |
常用命令Master命令
Volume命令
生成空的volumecurl "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 |
min.io vs seaweedfs - Object Store showdownI have tested our two "final" contenders on the Object Store category, ceph went down because of high complexity and high tech knowledge needed to minio advantages:
minio disadvantages:
seaweedfs advantages:
seaweedfs disadvantages:
|
SeaweedFS是一个独立的Apache许可的开源项目SeaweedFS是一个简单且高度可扩展的分布式文件系统。有两个目标:
SeaweedFS最初是一个Object Store,可以有效地处理小文件。中央主服务器不管理中央主服务器中的所有文件元数据,而是仅管理文件卷,并允许这些卷服务器管理文件及其元数据。这减轻了中央主服务器的并发压力,并将文件元数据传播到卷服务器,从而允许更快的文件访问(只需一次磁盘读取操作)。 每个文件的元数据只有40个字节的磁盘存储开销。使用O(1)磁盘读取非常简单,欢迎您使用实际用例挑战性能。 SeaweedFS首先实施了 Facebook的Haystack设计论文。 SeaweedFS只能在对象存储中很好地工作。然后可以稍后添加Filer以支持目录和POSIX属性。 Filer是一个单独的线性可伸缩无状态服务器,具有可自定义的元数据存储,例如,MySql / Postgres / Redis / Cassandra / LevelDB。 其他功能
Filer功能
使用示例默认情况下,主节点在端口9333上运行,卷节点在端口8080上运行。让我们在端口8080和8081上启动一个主节点和两个卷节点。理想情况下,它们应该从不同的计算机启动。我们将使用localhost作为示例。 SeaweedFS使用HTTP REST操作进行读取,写入和删除。响应采用JSON或JSONP格式。 启动主服务器
启动卷服务器
写入文件要上传文件:首先,向/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网址:
保存文件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”}]} 由于(通常)卷服务器不是太多,并且卷不经常移动,因此您可以在大多数时间缓存结果。根据复制类型,一个卷可以具有多个副本位置。只需随机选择一个位置即可阅读。 现在您可以通过网址获取公共网址,渲染网址或直接从卷服务器读取:
请注意,我们添加了一个文件扩展名“.jpg”这里。它是可选的,只是客户端指定文件内容类型的一种方式。 如果您想要更好的网址,可以使用以下其中一种替代网址格式:
如果您想获得图像的缩放版本,可以添加一些参数:
机架感知和数据中心感知复制SeaweedFS在卷级别应用复制策略。因此,当您获得文件ID时,您可以指定复制策略。例如: curl http://localhost:9333/dir/assign?replication=001 复制参数选项为:
有关复制的更多详细信息,请参阅维基上的 。您还可以在启动主服务器时设置默认复制策略。 在特定数据中心分配文件密钥可以使用特定数据中心名称启动卷服务器:
在请求文件密钥时,可选的“数据中心”参数可以将指定的音量限制为特定数据中心。例如,这指定分配的卷应限制为“dc1”:
其他功能
架构通常,分布式文件系统将每个文件分成块,中央主机保存文件名,块索引到块句柄的映射,以及每个块服务器具有的块。 主要缺点是中央主机无法有效处理许多小文件,并且由于所有读取请求都需要通过块主机,因此对于许多并发用户来说可能无法很好地扩展。 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旨在通过简单而扁平的架构快速存储和读取文件。主要区别是
与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 简单 磁盘相关主题硬盘性能测试读取性能时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://blog.guyskk.com/notes/rssant-story-storage 蚁阅 是一个 RSS 阅读服务, 全文阅读是其中一个很实用的功能,可以为读者提供流畅和一致的阅读体验。 早期在存储方面没有考虑太多,直接把文章信息和全文存储在 PostgreSQL 里面,随着订阅量越来越多, 这个表也越来越大(每个订阅至多保留 1K 篇文章),不久的将来会达到几千万这个量级。这会带来两个问题:
要解决 #1,需要对数据做分片,使用多个磁盘。要解决 #2,需要支持增量备份和恢复。 使用云厂商的对象存储方案,可以很好的解决这两个问题。 但是蚁阅有点特殊,首先我不希望绑定到任何云厂商,说不定哪天就被封了,这完全不可控; 其次蚁阅要支持独立部署,哪怕蚁阅服务没了,大家也能自己部署继续用这个产品。 不能使用云厂商的方案,那就找开源替代方案,调研下来大致有这些方案:
SeaweedFS方案我首先按 SeaweedFS 设计了一版存储方案。SeaweedFS 相当于 KV 存储,键称为 fid。 例如: 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 但是蚁阅更新订阅时是批量写入文章,我发现这个操作很慢。 所以问题可能是 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 行数据。 写入操作,支持批量写入: 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 比较差。结果仅供参考。 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 |
大佬,seaweedfs 两地三中心同步方案有参考建议吗? |
海草介绍
Seaweedfs是一个非常优秀的golang开发的分布式存储开源项目,专注解决小文件存储。开发者是中国人Chris Lu。Seaweedfs的设计原理是基于Facebook的一篇图片存储系统的论文Facebook-Haystack。目前Star数量7123。据称京东的tiglabs/containerfs其中关键的数据存储部分代码抄袭自海草。
名词术语
来源
Master Server, Volume Server, Filer Server
数据中心、机柜、存储
The text was updated successfully, but these errors were encountered: