couchbase 是一个较新的, 发展迅速的 nosql 数据库技术. 2014 年, viber 宣布使用 couchbase 替换 MongoDB, 以适应 10 亿级的用户量, 目前, couchbase 已大量运用于生产环境, 国内使用的公司主要有新浪, 腾讯等. 由于中文资料较少, 阅读了官方文档中的部分介绍后, 将资料翻译和汇总如下.
概述
couchbase 是 CouchDB 和 MemBase 的合并. 而 memBase 是基于 Memcached 的. 因此 couchbase 联合了 couchbase 的简单可靠和 Memcached 的高性能, 以及 membase 的可扩展性.
灵活的数据模型: couchbase 中使用 JSON 格式存储对象和对象之间的关系.
Nosql 数据库的一个特性是不需要定义数据结构, 在 couchbase 中, 数据可以存储为 key-value 对或者 JSON 文档, 不需要预先定义严格的格式, 由于这种特性, couchbase 支持以 scale out(水平扩展)方式扩展数据量, 提升 io 性能, 只需要在集群中添加更多的服务器就行了. 相反, 关系数据库管理系统 scale up(纵向扩展), 通过加更多的 CPU, 内存和硬盘以扩展容量.
Couchbase 可用于单机环境, 也可以和其他服务器一起提供分布式的数据存储.
数据存储:
Couchbase 通过使用 buckets 提供数据管理服务, buckets 相当于关系数据库中的库, couchbase 中没有表的概念, 保存数据时, 先建 bucket, 然后就直接插入数据了. buckets 可以供集群中的多个客户端程序访问. couchbase 通过 Buckets 组织, 管理和分析数据资源.
couchbase 中有两种类型的数据 bucket, 当启动 couchbase 服务的时候, 可以选择需要的类型.
1)Memcached buckets. 只将数据存储在内存中. 提供了一个分布式的(横向扩展), 纯内存的, key-value 缓存. Memcached buckets 设计用于关系数据库的缓存, 可以缓存经常访问的数据, 由此减少 web 程序中数据库的查询次数.
2)couchbase buckets. 存数据在内存和硬盘. 提供高可用性和可动态重新配置的分布式数据存储, 提供数据持久化和复制服务. couchbase buckets 100% 兼容开源的分布式缓存 Memcached.
内存配额
Server Quota:
Couchbase 服务初始化时会给服务器分配内存限额, 表示这个服务器中可用的最大内存, 是 node 级的. 初始配置在集群中的第一台服务器 (node) 上, 所有服务器的内存配额都是一样的. 例如集群中有 10 台服务器, 服务器内存配额是 16G, 整个集群中共 160G 可用内存. 如果需要加 2 个新的服务器, 每个新的服务器需要 16G 的可用内存, 集群中可用的内存数将是 192G.
Bucket Quota:
Bucket 内存配额是分配给一个 bucket 的可用内存. 配置在每个节点上, 是从 server quota 中分配出去的. 例如, 如果你创建了一个新的 bucket, 限额是 1GB, 在 10 个节点的集群中, 汇总后会有 10G. 如果添加两个节点, 集群中汇总后会有 12G 的 bucket 限额.
从图上可以看出, 增加新的节点就可以扩展总的可用内存, 从而增加存储的数据量. 通过 bucket 限额系统可以判断数据是否应该调出内存.
vBucket
vBucket 相当于一个 key 的子集, 保存的是客户端存储对象的 key 值, vBuckets 用于在集群的节点间分配数据和备份数据. 是 couchbase 实现 auto sharding, 在线动态增减节点的重要基础. 不是用户可访问的组件, 但是至关重要.
通过 vbucket, 客户端直接访问保存信息的服务器, 不需要通过中间代理或者其他架构, 因此可以从数据中抽象出物理拓扑结构. 这种方式使 couchbase 易扩展.
这种架构不同于 Memcached 使用的架构, Memcached 的做法是用 key 算出一个 hash, 得到服务器列表中的对应服务器. 这个列表需要动态维护, 还需要一个 hash 算法用于处理集群拓扑结构的变化.
如以下代码所示:
- servers = ['server1:111', 'server2:112', 'server3:113']
- server_for_key(key) = servers[hash(key) % servers.length]
这种算法很简单, 也很容易理解, 但也有几个问题:
1, 如果一台服务器失效, 会造成该分片的所有 key 失效.
2, 如果服务器容量不同, 管理非常麻烦.
3, 运维, 配置非常不方便.
为了把 key 跟服务器解耦, couchbase 引入了 vBucket. 每个 key 都属于一个 vbucket, 查找对应的 value 时先用 hash 函数计算这个 key 属于哪个 vbucket, 再从 vBucket 与服务器对应表中查找这个 vbucket 属于哪个服务器, 映射表保存 vbucket 和服务器的对应关系, 一个 bucket 一行, 一个服务器可以对应多个 vbucket.
1,key hash 对应一个 vBucket, 不再直接对应服务器.
2, 集群维护一个全局的 vBucket 与服务器对应表.
例如, 集群中有 3 个服务器, 客户端要查找 一个 key 对应的 value 值, 首先计算 key 属于哪个 Vbucket, 在这个例子中, hash 结果是 vB8 , 通过查映射表, 客户端确定 vB8 对应到服务器 C, 然后 get 操作直接发送到服务器 C.
一段时间后, 需要加一个新的服务器 D 到集群, vbuckets 映射表更新为:
这时, 客户端再想取 key 对应的 value 值, hash 算法结果仍为 vB8, 但是新的映射表会将 vB8 映射到服务器 D.
由于 vBucket 把 key 跟服务器的静态对应关系解耦合, 基于 vBucket 可以实现一些非常强大有趣的功能, 例如:
Replica, 以 vBucket 为单位的主从备份. 如果某个节点失效, 只需要更新 vBucket 映射表, 马上启用备份数据.
动态扩容. 新增加一个节点后, 可以把部分 vBucket 转移到新节点上, 并更新 vBucket 映射表.
缓存层
Couchbase 自动管理缓存层, 确保有足够的内存空间以维持性能. couchbase 后台有个进程, 专门把一定时间没有被访问的数据移出内存, 这个进程的扫描时间和数据的最大无活动时间都是可以设置的. couchbase 在对数据进行增删时会先体现在内存中, 而不会立刻体现在硬盘上, 从内存的修改到硬盘的修改这一步骤是由 couchbase 自动完成, 等待执行的硬盘操作会以 write queue 的形式排队等待执行, 也正是通过这个方法, 硬盘的 I/O 效率在 write queue 满之前是不会影响 couchbase 的吞吐效率的, 而 write queue 的长度是可以设置的. 通过 write queue 执行大量写数据库操作时用户可能会感受到很短时间的内存飙升, 但是异步特性和 queue 的使用使读写速度都非常快.
对于所有文档 couchbase 都会建立一个额外的 56byte 的 metadata, 这个 metadata 功能之一就是表明数据状态是否活动在内存中. 同时文档的 id 也作为标识符和 metadata 一起长期活动在内存中. 这表示对文档 id 不存在的情况服务器可以直接返回'文档 id 不存在. couchbase 官方建议 bucket 申请的内存中, metadata 和 key 所占用的内存不应超过一半, 否则 couchbase 的性能会显著下降. 为了保证这个条件, 当有效数据占用超过一定内存时就需要把超额数据移除了.
这里有低水位和高水位的概念, 也就是说当移除数据过多以至于内存中有效数据占用内存低于低水位的时候, couchbase 会随机挑一些文件到内存中以达到低水位. 当有效数据内存占用超过高水位时, couchbase 就会移除数据. 高低水位都是可以设置的.
随着内存数据越来越多, 会逐渐到达低水位, 这时候, 系统不会做任何处理. 当数据量持续增加, 到达高水位时, 系统会启动一个 job 任务移除数据, 当到达低水位时任务停止. 如何进入数据的速度大于移除数据的速度, 系统会返回空间不足的错误提示, 直到有足够的内存为止.
如果只使用 Memcached buckets, 服务器只提供数据缓存, 不会持久化到硬盘. 如果服务器内存空间不足, 系统会使用 LRU 算法从内存中 Eviction 数据, Eviction 表示服务器会移除一个数据项的 key,metadata 和所有其他信息. Eviction 后, 数据将无法恢复.
硬盘存储
考虑到性能, couchbase 服务主要使用缓存为客户端保存和返回信息, 同时会逐渐将数据保存到硬盘以维持高可靠性. 如果有节点 fail 了可以直接从硬盘恢复数据, 数据是逐渐持久化到硬盘的, 同时保存到缓存层和硬盘 write queue, 不会阻塞客户线程. 当客户端访问一个不在内存的数据时, 持有 load queue 的进程会将数据读到内存, 在数据返回之前客户端需要等待.
多线程读写: 之前的 couchbase 版本, 读写硬盘的线程只有一个. 为了提高读写硬盘的速度和提高缓存命中率, 现在 couchbase 提供了多线程读写.
多线程读写需要考虑线程之间的同步问题以避免冲突. 为了维持性能和避免冲突, couchbase 对每个线程访问的资源进行了静态分配, 同时使用了资源锁. 当创建多个读写线程时, 服务器为每个线程单独分配了不同的 vbuckets. 通过这种静态协调方式, 可以保证同一个 vbuckets 只有一个读线程和一个写线程可以访问. 上图表示 6 个线程和两个数据 vbucket. 每个线程有预先分配的可读写范围.
Rebalancing
Couchbase 服务器上数据的分配方式是通过 vbucket 结构实现的, 如果想增加或减少集群中的机器, vbuckets 中存储的数据都需要重新分配, vBucket map(映射表)也需要更新以适应新的集群结构.
Rebalancing 是集群结构发生变化时的数据调整, 需要手动操作, 这个过程改变 vbuckets 到服务器的分配, 需要在服务器节点之间迁移数据以适应新的结构.
Rebalancing 过程可以发生在集群运行和提供服务时, 客户端使用现有架构读写数据, 同时后端在节点之间迁移数据. 一旦这个过程完成, 更新 vBucket map 表, 并通知 smart clients 和代理服务(MOxi).
Rebalancing 后数据在集群中重新分配, 因此数据被均匀分配到整个数据库, 这个过程需要考虑数据和数据的副本.
总结
数据存储: nosql, 易水平扩展.
缓存层: 低水位和高水位设置, 文档 metadata.
vBucket: 使系统易于动态管理, 自动分片, reblancing.
多线程持久化: 预分配每个线程的访问范围, 保证性能和可靠性.
来源: http://www.bubuko.com/infodetail-2974300.html