源地址:http://idning.github.io/ssd-cache.html
Table of Contents
1 需求
1.2 具体需求
- 数据存放在ssd.
- 性能要求: 6台机器的集群10w/s, (单机2w/s)
- 有expire功能.
- 使用redis协议 (twemproxy, client-lib可以复用)
- 数据类型仅支持kv, 以后可以考虑支持hash.
其它:
- 事务或script支持.
- 主从, failover
- 集群.
- redis-mgr 部署支持
2 index
主要涉及下面几个方面:
- ssd特性.
- 存储引擎, 如LevelDB, RocksDB, BDB等.
- 现有系统的调研和benchmark, 主要关注SSDB和fatcache.
本文是这个调研系列的目录和结论, 相关调研:
- coding-for-ssd笔记
- LevelDB 调研 TODO
- SSDB代码阅读
- SSDB benchmark
- fatcache 代码阅读
3 ssd 特性
3.1 成本
-- | 国外 | 国内 | 2T成本 |
---|---|---|---|
ssd | $0.6/GB | 京东价格(400元/128G=3.1元/GB) | 6000 |
hdd: | $0.12/GB | 京东价格(400元/1T=0.4元/GB) | 800 |
- 实际上, 我们买服务器的时候, 价格会更便宜些, 不过还是这个数量级.
- 考虑到一台1U服务器价格 在3-5w, 使用2T ssd带来的成本上升: 5200/30000 = 18%左右, 并不算太贵, 加之后续电费等消耗, 可以认为使用ssd带来的成本上升小于15%
- 当然, 我们不能用ssd来存文件之类的大/冷的数据, 这是明显的浪费.
3.2 接口
目前ssd主要2种接口:
-
sata
-
-
pci-e, 性能更高.
-
-
-
典型产品如:
-
- Fusion-io ioScale Gen2 (w: 4w, r:5w)
- Fusion-io ioMemory (w: 32w, r:19w)
- 华为ES3000 (w: 10w, r:15w)
- MemblazeQ520 (w:7w, r:3w)
-
-
-
sata 带宽6Gbps, pci-e 常见带宽 3.2G*8 = 24Gbps.
3.3 比较典型的ssd参数
Product | Intel SSD 320 | Intel SSD 530 | ioMemory PX600 |
---|---|---|---|
Components | MLC | MLC | MLC |
Launch Date | 2011 | 2013 | |
Sequential Read | 270 MB/s | 540 MB/s | 2700 MB/s |
Sequential Write | 220 MB/s | 490 MB/s | 1500MB/s |
Random Read (8GB Span) | 39,500 IOPS | 48,000 IOPS | 196,000 IOPS |
Random Write (8GB Span) | 23,000 IOPS | 80,000 IOPS | 320,000 IOPS |
Latency - Read | 75 us | 80 us | 92 us |
Latency - Write | 90 us | 80 us | 15 us |
interface | SATA 6.0 Gb/s | SATA 6.0 Gb/s | PCI-Express 2.0 x8 |
数据来源:
- Intel-SSD-320: http://ark.intel.com/products/56569/Intel-SSD-320-Series-600GB-2_5in-SATA-3Gbs-25nm-ML
- Intel-SSD-530: http://ark.intel.com/products/75336/Intel-SSD-530-Series-480GB-2_5in-SATA-6Gbs-20nm-MLC
- Fusion-io: http://www.fusionio.com/data-sheets/iomemory-px600-atomic-series/
notes:
-
这里选的 Fusion-io ioMemory系列, 写可以达到32w/s, 写延迟只有15us, 很明显写操作都是先写buffer.
-
-
对三块Intel-SSD-530 做raid0后, 用fio进行了测试, 数据和标称数据差不多:
-
- random-write: 5.5w/s
- random-read: 7.3w/s
-
3.4 小结
-
随机读性能好
-
-
随机写性能较差
-
-
写放大: 写一个字节也会导致整个page的read-modify-write
-
应该尽量避免small-write
-
-
很多ssd会通过
hybrid log-block mapping 来做写merge. 从而减轻写放大,
-
- 这相当于把Log-Structure的一些算法在ssd控制器这一层实现了, 从而实现较高的随机写性能.
- 但是即便有了 hybrid log-block mapping, 也应该尽量避免small-write(因为需要多次操作映射关系表)
-
-
-
ssd在大量写压力下, 性能可能恶化到8000iops.
-
- 因为很多update, GC可能跟不上, 如果每次写操作需要做一次erase整个block, 就悲剧了.
- 正常情况下, GC利用后台的时间, 可以完成erase工作.
-
-
-
-
顺序读写和hdd在同一量级.
-
寿命有限
ssd可以通过下面这些方式调优:
- 调整page/block的大小, 较小的擦除块可以得到较高的wqps.
- gc策略: 可以通过不同的算法优化, 这是ssd控制器FTL的核心技术.
- Flash Translation Layer (FTL) 上做 hybrid log-block mapping 优化随机写.
- 使用 TRIM 命令, 会有少量优化.
详细参考: coding-for-ssd笔记
4 现有系统调研
针对我们的需求, 调研了一些现有的系统, 主要分三类:
- 基于redis的修改如redis-vm.
- 单机引擎如Berkley DB, LevelDB.
- 一些和我们需求接近的现有系统, 如ssdb, fatcache等.
- 成熟产品, 如淘宝tair, aerospike 等.
4.1 基于redis修改
4.1.1 redis-vm
redis在2.2-2.4曾经做过vm功能, 来将内存扩展到磁盘, 但是不久就被废弃了, 原因主要是造成性能不稳定.
存在的问题:
- slow restart: 重启太慢
- slow saving: 保存数据太慢
- slow replication: 上面两条导致 replication 太慢
- complex code: 代码过于复杂
- 2.4 之后就已经从redis代码中移除了.
作者的观点:
- have Redis do what it does best - very quickly serve data from RAM.
- 估计当时的测试, 使用的磁盘都是hdd, 那当然性能糟糕, 如果换成ssd应该会好些.
4.1.2 redis-storage
-
把leveldb嵌入到redis.
-
完成度较高, 新增了一些rl_开头的命令:
rl系列命令:(同时操作redis和leveldb系列命令) =======string数据操作====== rl_get key (从redis或leveldb取值, 优先顺序:redis > leveldb) rl_getset key (返回同rl_get, 当leveldb有值,redis无值时,会回写到redis) ...
-
读: 先从redis读取, 如果redis没有,则到leveldb读取。
-
写: 先写到leveldb中,写成功了,再写到redis中
-
-
问题:
-
-
这个项目的目的是把redis的内存扩大2-5倍, 把redis作为leveldb的cache+store. 两份storage很诡异.
-
-
作者设计的时候, 应该是考虑到兼容redis, 客户端尽量不需要改动, 冷key会自动淘汰,
-
- 但是实际上提供了两套命令, 客户端需要根据情况, 指定只写redis/只写leveldb还是双写. 就很麻烦.
-
-
没有expire支持, leveldb过大后, 怎么办?
-
完全没有考虑到主从的设计.
-
-
参考:
4.1.3 小结
基于redis的改进, 主要有这么几种:
- 增加新命令
- 在key被淘汰时写磁盘
- key一直在内存, 把某些value放磁盘(redis-vm的实现方案)
如果基于redis来实现, 存在下面一些问题:
- 主从同步很可能被破坏(现有全量同步机制需要重新改写)
- 重启时加载数据的机制.
- 不能支持全部命令, 容易造成混淆.
- 不能被主流所接受
如果不基于redis代码来做:
- 主从同步需要重做
- sentinel机制需要重做.
4.2 单机存储引擎
4.2.1 LevelDB
LevelDB是BigTable的单机存储, LSM-Tree 思想, 写操作都转化为顺序写.
特点:
-
KV引擎
-
支持SCAN(iteration)
-
Snappy压缩
-
-
随机读写能达到 10w/s 的性能
-
- 这里性能是小数据量下, 还不刷盘的情况
- 实际写能到10w, 读取决于存储介质.
-
-
支持Bloom Filter, 能在一定程度上优化读性能.
4.2.2 RocksDB(facebook)
- 基于LevelDB改进
- 更好的利用多核等
- 代码包比LevelDB复杂.
4.2.3 Berkley DB
-
历史悠久的嵌入式数据库
-
支持事务, 细粒度锁.
-
-
支持多种算法
-
- B+树
- Hash
- Heap(更节约空间)
- Recno
- Queue(定长record)
-
-
对一些老的UNIX数据库, 如dbm, ndbm接口兼容.
http://docs.oracle.com/cd/E17076_02/html/programmer_reference/am_conf.html
4.2.4 nessDB(国人开发)
-
支持事务 特点
-
-
自己实现存储引擎, 不是基于LevelDB
-
- 3.0 提供 Buffered-Tree index (Toku的FT-Tree)
- 作者是TokuDB的贡献者.
-
-
2011-2014持续开发, 目测代码质量很高.
-
本身是一个库.
-
-
还提供一个服务端,支持Redis的 PING, SET, MSET, GET, MGET, DEL, EXISTS, INFO, SHUTDOWN 命令,
-
- 现在已经专注于实现引擎, 不提供server功能了.
-
-
整个引擎基于LSM-Tree思想开发,对随机写非常友好。为提高随机读,nessDB使用了Level LRU和Bloom Filter策略。
-
引擎是自己开发的, 还需要时间验证.
4.2.5 小结
- 较老的存储引擎都基于B+树或Hash实现, 写性能差.
- 较新的存储引擎基于LSM-Tree, Log Structed Hash, FT-Tree之类新的数据结构, 针对写进行优化, 写性能能得到很大改善
- 读操作主要取决于底层磁盘能提供的 随机读IOPS , 通过Bloom Filter等能有一定的优化.
4.3 备选项目
4.3.1 ssdb
-
ssdb 是一个基于leveldb的kv存储, 提供兼容redis的协议
-
支持String, Hash, Zset, Queue几种数据结构.
-
支持Expire和主从同步
-
ssd上写性能稳定在3.8wqps, 不会随着写数据增多而变差, 和hdd差不多,
-
读性能稳定在5000qps, 不能充分发挥硬件性能, 这主要是由于读操作是单线程顺序执行.
-
-
主要问题:
-
- 读性能问题(多线程可达到15000)
- 所有expire的key记录在内存
- 兼容问题(expire/ttl/del都有问题, scan类设计上和redis不同)
-
详细参考:
4.3.2 fatcache
-
Memcache on SSD
-
Log-Structure Hash结构.
-
不能持久化(元数据不落盘, 重启后数据丢失)
-
性能不错, Initial performance results with fatcache 100K sets/sec, 40K gets/sec on a single SSD
-
读不命中时效率高(所有key记录在内存中)
-
写裸盘. 需要root.
-
-
主要问题:
-
-
不持久化
-
-
key都放在内存, 如果10亿条的话, 每条key 32字节, 就需要32G. 此时存的数据(100字节/kv) 大约100G.
-
- 所以这适合value较大的情况, 比如1K, 这样32G内存就能管理1T数据.
-
-
-
详细参考: fatcache代码阅读笔记
4.3.3 ardb
https://github.com/yinqiwen/ardb
- Full redis-protocol compatible
- Most redis commands supported, and a few new commands
- Replication compatible with Redis 2.6/2.8
- Auto failover support by redis-sentinel
- 存储引擎支持 LevelDB/LMDB/RocksDB
- 空间索引.
- 代码量5w, 很难想象是一个人的作品. (HUST)
看上去很不错, c++实现.
4.3.4 ledisdb
- go 实现(金山 siddontang)
- https://github.com/siddontang/ledisdb
- 支持多种引擎: LevelDB, goleveldb, LMDB, RocksDB, BoltDB.
- 支持expiration
- 比GoRedis完善
- 写的很细心
- lua
- redis 协议 +rest协议
设计:
4.3.5 其它
-
-
redis-leveldb
-
- 不是基于redis的代码,
- https://github.com/KDr2/redis-leveldb
- redis-protocol compatible
- libev, cpp实现.
- 2000行代码, 简单实现的玩具, 代码中各种printf直接输出到终端. 代码质量差
- 没有expire, 主从同步,
-
-
-
seqdb
-
- 提供sql接口的kv.
-
-
-
GoRedis
-
- go实现, (陌陌)
- 存储引擎使用RocksDB, redis接口.
- 不支持expire
- slaveof-proxy为两个redis建立自定义的主从同步,包含限速、断线重试等。
-
-
-
rdisk 是一个hackathon项目, 提供兼容redis的协议.
-
- libuv做服务
- rangel 解析
- tokyocabinet 作为存储引擎(作为.so嵌入)
- https://github.com/Moodstocks/redisk
- 是一个不错的开始
-
-
-
lycadb
-
- An experiment with InnoDB storage for a Redis-like key/value store
-
-
-
redis-land-go
-
- https://github.com/xjdrew/redis-land-go
- 旁路监听,把redis数据存盘到leveldb
-
4.3.6 小结
- 有很多尝试做兼容redis的磁盘存储的项目,
- 在设计实现上都存在或多或少的问题.
4.4 成熟分布式存储系统
4.4.1 淘宝tair
-
淘宝开发的分布式 key/value 存储系统
-
-
模块
-
- config-server (master+slave)
- data server (存储节点)
- 客户端保存路由表, 有local cache
- 一致性hash+数据迁移
-
-
-
存储引擎
-
- mdb: 缓存, 支持kv,
- rdb: redis内存结构, kv, list, set, zset.
- ldb: 基于leveldb.
-
-
需要专用的 client lib.
-
支持多副本, 多版本.
-
-
规模(2011):
-
- 共有20多个集群,400多台的服务器,其中300多台是cache的,每台提供22G的内存。其他的是持久化的Tair集群。
- 存放了数百亿条记录,每秒百万级别的请求数。
-
4.4.2 aerospike
-
商业产品, 开源.
-
-
三层:
-
- client 感知数据存在哪里
- Distribution Layer
- Data Storage Layer: 单机引擎.
-
-
API形式: 不是简单的key/value, 每个key需要指定 (namespace, set, key), 应该是为了控制锁粒度.
-
Indexes are always stored in RAM.
-
数据类型: map, list, integer, string, blob.
-
可配 expire
-
支持lua.
-
支持Secondary indexes, 不是简单kv, 更像mongo
-
支持Hot Analytics (distributed aggregations or indexed map-reduce)
4.4.3 #facebook Apollo
http://www.infoq.com/news/2014/06/facebook-apollo
- Paxos-like NoSQL database
- C++
- 低延迟, 特别是Flash and in-memory
- 不是简单kv, 支持数据结构: maps, queues, trees
- 分布式, 有shard概念, 每个shard内基于RocksDB.
- Apollo isn't currently being used in production at Facebook
- The company is also looking at using Apollo as a reliable queuing system
- 是分布式的ssdb
- 还没开源.
4.4.4 #facebook-McDipper
Compared with memory, flash provides up to 20 times the capacity per server and still supports tens of thousands of operations per second,
- 大约12年5月上线.
- cache替换策略: LRU或者FIFO
- you can enable bloom filters to avoid unnecessary reads
- 主要用于图片服务器的缓存(cdn上) 后端是HayStack.
- We serve over 150 Gb/s from McDipper forward caches in our CDN.
- 是一个cdn用的cache存储. memcache协议.
- 不开源.
4.4.5 #腾讯CKV海量分布式存储系统
(闭源, 这里参考一个ppt)
http://www.csdn.net/article/2014-03-11/2818723
高性能、低延时、持久化、分布式KV存储服务, 日请求数: 超过万亿次 (那得看多少套集群)
与Memcached和Redis等开源NoSQL相比,CKV具有以下优点.
-
内存+SSD, 99%命中率(取决于应用)
-
可以扩展到1PB
-
单表 千万qps
-
单台Cache服务器千兆网络环境支持50万/秒的访问,万兆网络环境支持超过100万/秒的访问 <redis也10个实例, 也可以做到>
-
双机热备,主备切换对业务透明. redis一样.
-
-
扩容: 需要停写
-
- 扩容过程如下:Master将禁止shard2数据写访问命令发送给Access
-
-
-
规模: 近万台服务器
-
- 万台, 每天万亿请求, 那就是说1亿/台, 每台只相当于1000qps.
-
4.4.6 小结
- 这里的几个系统, 都是类似GFS的架构, config-server + data server + 智能client
- 支持动态数据迁移和路由更新.
- 它们都有各自的接口, 相对来说比较复杂.
4.5 其它思路
-
-
HBase/Cassendra/MySQL on ssd?
-
- 可以利用现有hbase等系统良好的扩展性, 性能上也能有所保证
- 接口不兼容.
- 过于复杂.
-
5 小结
-
-
为了避免随机写, 现在很多存储引擎都是Write-optimized的, 基于LSM的思想来开发, 比如:
-
- fatcache/Riak: Log-Structure Hash table
- RethinkDB: Log-Structure B-tree
- LevelDB, Cassendra, HBase: Log-Structure merge tree.
-
-
LevelDB 平均每次读大约需要1.3-1.5次IO, Log-Structure Hash 只需要1次, 但是不能scan.
-
ssdb/ardb/nessDB/ledisdb 都是国人做的, 很赞, 值得持续关注.
-
可以否决基于redis做的改造.
-
基于LevelDB或者RocksDB封装提供redis协议比较简单, 难点主要是expire/replication/failover的实现.
5.1 关于目前的很多系统
无论对redis改造或者是重新实现, 在数据结构/expire/主从同步上, 都延用了redis的做法, 比如:
-
支持富数据结构
-
expire信息放在内存
-
主从断掉全量同步.
-
-
依然有rdb, 整个库占了1T磁盘, rdb出来也占1T => 这些设计可能没考虑1T这个数据量级, 只考虑100G这个量级.
-
- 比如ardb依然使用rdb做同步.
-
-
甚至有的还会把磁盘操作计入aof, 再加上aof_rewrite,
由于数据规模变大(60G=>600G的级别) 这些设计就存在问题.
代码 | 特点 | expire | repl | 其它 | ||
---|---|---|---|---|---|---|
2 | redis-storage | redis+特定命令 | 无 | 无 | 专注于使内存成为磁盘的cache | |
3 | ssdb | 单独key存储 | ||||
3 | ardb | 5w | cpp, 复杂, 地理索引 |
| 兼容性好, 沿用redis, 依然支持rdb | |
3 | ledisdb(go) 金山 | 2w | 单独key存储, 定期elim |
| ||
2 | GoRedis | 2w |