Permalink: 2014-05-13 06:56:03 by ning in redis tags: all

1   需求

1.1   why

用redis内存实在太贵了, 假设要存1T数据双副本:

  • 内存: 1000*2 / 64 = 32台机器.
  • 2T盘机器: 2-4台

1.2   具体需求

  1. 数据存放在ssd.
  2. 性能要求: 6台机器的集群10w/s, (单机2w/s)
  3. 有expire功能.
  4. 使用redis协议 (twemproxy, client-lib可以复用)
  5. 数据类型仅支持kv, 以后可以考虑支持hash.

其它:

  • 事务或script支持.
  • 主从, failover
  • 集群.
  • redis-mgr 部署支持

2   index

主要涉及下面几个方面:

  1. ssd特性.
  2. 存储引擎, 如LevelDB, RocksDB, BDB等.
  3. 现有系统的调研和benchmark, 主要关注SSDB和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

数据来源:

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的改进, 主要有这么几种:

  1. 增加新命令
  2. 在key被淘汰时写磁盘
  3. key一直在内存, 把某些value放磁盘(redis-vm的实现方案)

如果基于redis来实现, 存在下面一些问题:

  1. 主从同步很可能被破坏(现有全量同步机制需要重新改写)
  2. 重启时加载数据的机制.
  3. 不能支持全部命令, 容易造成混淆.
  4. 不能被主流所接受

如果不基于redis代码来做:

  1. 主从同步需要重做
  2. 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   其它

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集群。
    • 存放了数百亿条记录,每秒百万级别的请求数。

http://code.taobao.org/p/tair/wiki/intro/

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)

https://github.com/aerospike/aerospike-server

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

https://www.facebook.com/notes/facebook-engineering/mcdipper-a-key-value-cache-for-flash-storage/10151347090423920

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
  • 存储: 自定义binlog,fid+offset
  • 同步: 从拉,全量+增量
  • 切换: 全量同步
 
2 GoRedis 2w        

Comments