logo资料库

分布式存储.pdf

第1页 / 共83页
第2页 / 共83页
第3页 / 共83页
第4页 / 共83页
第5页 / 共83页
第6页 / 共83页
第7页 / 共83页
第8页 / 共83页
资料共83页,剩余部分请下载后查看
目录
Memcached
Memcached架构
Memcache实现分析理解
实现结构
内存分配管理
在Slab中缓存记录的原理
Slab Allocator的缺点
memcached在数据删除方面有效利用资源
数据不会真正从memcached中消失
Lazy Expiration
LRU:从缓存中有效删除数据的原理
memcached的最新发展方向
关于二进制协议
二进制协议的格式
HEADER中引人注目的地方
外部引擎支持
外部引擎支持的必要性
简单API设计的成功的关键
重新审视现在的体系
分布处理
Memcached的特点
分布式算法
memcached的分布式
memcached的分布式是什么意思?
Cache::Memcached的分布式方法
根据余数计算分散
根据余数计算分散的缺点
Consistent Hashing
Consistent Hashing的简单说明
支持Consistent Hashing的函数库
Memcached 故障处理
故障的类型:
Rehash处理机制
Memcached应用
应用说明
应用案例
mixi案例研究
Fotolog
Memcached优点
Memcached缺点
Memcached性能
Sina Memcached BDB
系统架构
实现结构
Tokyo Tyrant
系统架构实现
简介和特点
故障转移
日志文件体积小
超大数据量下表现出色
性能高
Redis
Facebook Memcached MySQL
MySQL Sharding策略
Facebook Cassandra
简介
Cassandra的特点如下:
系统架构
1、目的
2、功能概要
3、数据类型描述
4、Partitioning
5、副本机制
NWR
副本策略
6、故障和扩展
路由更新
数据迁移
Gossiper:
7、Service
StorageProxy——存储服务
AntiEntropyService——同步/错误解决服务
EmbeddedCassandraService——嵌入式in-memory存储服务
StorageLoadBalancer——负载均衡服务
Cassandra数据模型
Column
SuperColumn
Column Family
Keyspace
简单测试
排序
Cassandra存储机制
Cassandra 相关理论
Bloom Filter概念和原理
集合表示和元素查询
错误率估计
最优的哈希函数个数
位数组的大小
总结
参考资料
故障处理
事务处理
性能
和其他DB的比较
HBase vs Cassandra: 我们迁移系统的原因
Yahoo! Cloud Serving Benchmark
应用场景
Google BigTable
分析总结
Xz DB
系统架构
功能设想
相关问题
性能考量
参考
分布式存储及应用系统架构分析 文件更改摘要: 修订记录 日期 2010-4-4 创建 修订说明 修订人 批准人 龙兴平
目录 1 Memcached 1.1 Memcached 架构 memcached 是高性能的分布式内存缓存服务器。 一般的使用目的是,通过缓存数据库查询结 果,减少数据库访问次数,以提高动态 Web 应用的速度、 提高可扩展性。 龙兴平 (MSN: lxp8@sina.com) 第 2 页 共 83 页 2010-4-27
1.2 Memcache 实现分析理解 1.2.1 实现结构 Speed Of course, the primary motivation for caching is speed, so Memcached is designed to be as fast as possible. The initial prototype of Memcached was written in Perl. Although I love Perl, the prototype was laughably slow and bloated. Perl trades off memory usage for everything, so a lot of precious memory was wasted, and Perl can't handle tons of network connections at once. The current version is written in C as a single-process, single-threaded, asynchronous I/O, event-based d?mon. For portability and speed, we use libevent (see the on-line Resources section) for event notification. The advantage of libevent is that it picks the best available strategy for dealing with file descriptors at runtime. For example, it chooses kqueue on BSD and epoll on Linux 2.6, which are efficient when dealing with thousands of concurrent connections. On other systems, libevent falls back to the traditional poll and select methods. Inside Memcached, all algorithms are O(1). That is, the runtime of the algorithms and CPU used never varies with the number of concurrent clients, at least when using kqueue or epoll, or with the size of the data or any other factor. Of note, Memcached uses a slab allocator for memory allocation. Early versions of Memcached used the malloc from glibc and ended up falling on their faces after about a week, eating up a lot of CPU space due to address space fragmentation. A slab allocator allocates only large chunks of memory, slicing them up into little chunks for particular classes of items, then maintaining 龙兴平 (MSN: lxp8@sina.com) 第 3 页 共 83 页 2010-4-27
freelists for each class whenever an object is freed. See the Bonwick paper in Resources for more details. Memcached currently generates slab classes for all power-of-two sizes from 64 bytes to 1MB, and it allocates an object of the smallest size that can hold a submitted item. As a result of using a slab allocator, we can guarantee performance over any length of time. Indeed, we've had production Memcached servers up for 4–5 months at a time, averaging 7,000 queries/second, without problems and maintaining consistently low CPU usage. Another key requirement for Memcached was that it be lockless. All objects are multiversioned internally and reference counted, so no client can block any other client's actions. If one client is updating an object stored in Memcached while a dozen others are downloading it, even with one client on a lossy network connection dropping half its packets, nobody has to wait for anybody else. A final optimization worth noting is that the protocol allows fetching multiple keys at once. This is useful if your application knows it needs to load a few hundred keys. Instead of retrieving them all sequentially, which would take a fraction of a second in network round-trips, the application can fetch them all in one request. When necessary, the client libraries automatically split multi-key loads from the application into separate parallel multi-key loads to the Memcached instances. Alternatively, applications can provide explicit hash values with keys to keep groups of data on the same instance. That also saves the client library a bit of CPU time by not needing to calculate hash values. Ref: http://www.linuxjournal.com/article/7451?page=0,2 Memcached 是 danga.com(运营 LiveJournal 的技术团队)开发的一套分布式内存对象缓存 系统,用于在动态系统中减少数据库负载,提升性能。关于这个东西,相信很多人都用过, 本文意在通过对 memcached 的实现及代码分析,获得对这个出色的开源软件更深入的了解, 并可以根据我们的需要对其进行更进一步的优化。末了将通过对 BSM_Memcache 扩展的分 析,加深对 memcached 的使用方式理解。 本文的部分内容可能需要比较好的数学基础作为辅助。 ◎Memcached 是什么 在阐述这个问题之前,我们首先要清楚它“不是什么”。很多人把它当作和 SharedMemory 那种形式的存储载体来使用,虽然 memcached 使用了同样的“Key=>Value”方式组织数据, 但是它和共享内存、APC 等本地缓存有非常大的区别。Memcached 是分布式的,也就是说 它不是本地的。它基于网络连接(当然它也可以使用 localhost)方式完成服务,本身它是一 个独立于应用的程序或守护进程(Daemon 方式)。 Memcached 使用 libevent 库实现网络连接服务,理论上可以处理无限多的连接,但是它和 Apache 不同,它更多的时候是面向稳定的持续连接的,所以它实际的并发能力是有限制的。 在保守情况下 memcached 的最大同时连接数为 200,这和 Linux 线程能力有关系,这个数值 龙兴平 (MSN: lxp8@sina.com) 第 4 页 共 83 页 2010-4-27
是可以调整的。关于 libevent 可以参考相关文档。 Memcached 内存使用方式也和 APC 不同。 APC 是基于共享内存和 MMAP 的,memcachd 有自己的内存分配算法和管理方式,它和共 享内存没有关系,也没有共享内存的限制,通常情况下,每个 memcached 进程可以管理 2GB 的内存空间,如果需要更多的空间,可以增加进程数。 ◎Memcached 适合什么场合 在很多时候,memcached 都被滥用了,这当然少不了对它的抱怨。我经常在论坛上看见有 人发贴,类似于“如何提高效率”,回复是“用 memcached”,至于怎么用,用在哪里,用 来干什么一句没有。memcached 不是万能的,它也不是适用在所有场合。 Memcached 是“分布式”的内存对象缓存系统,那么就是说,那些不需要“分布”的,不 需要共享的,或者干脆规模小到只有一台服务器的应用,memcached 不会带来任何好处, 相反还会拖慢系统效率,因为网络连接同样需要资源,即使是 UNIX 本地连接也一样。 在 我之前的测试数据中显示,memcached 本地读写速度要比直接 PHP 内存数组慢几十倍,而 APC、共享内存方式都和直接数组差不多。可见,如果只是本地级缓存,使用 memcached 是非常不划算的。 Memcached 在很多时候都是作为数据库前端 cache 使用的。因为它比数据库少了很多 SQL 解析、磁盘操作等开销,而且它是使用内存来管理数据的,所以它可以提供比直接读取数 据库更好的性能,在大型系统中,访问同样的数据是很频繁的,memcached 可以大大降低 数据库压力,使系统执行效率提升。另外,memcached 也经常作为服务器之间数据共享的 存储媒介,例如在 SSO 系统中保存系统单点登陆状态的数据就可以保存在 memcached 中, 被多个应用共享。 需要注意的是,memcached 使用内存管理数据,所以它是易失的,当服务器重启,或者 memcached 进程中止,数据便会丢失,所以 memcached 不能用来持久保存数据。很多人的 错误理解,memcached 的性能非常好,好到了内存和硬盘的对比程度,其实 memcached 使 用内存并不会得到成百上千的读写速度提高,它的实际瓶颈在于网络连接,它和使用磁盘 的数据库系统相比,好处在于它本身非常“轻”,因为没有过多的开销和直接的读写方式, 它可以轻松应付非常大的数据交换量,所以经常会出现两条千兆网络带宽都满负荷了, memcached 进程本身并不占用多少 CPU 资源的情况。 ◎Memcached 的工作方式 以下的部分中,读者最好能准备一份 memcached 的源代码。 Memcached 是传统的网络服务程序,如果启动的时候使用了-d 参数,它会以守护进程的方 式执行。创建守护进程由 daemon.c 完成,这个程序只有一个 daemon 函数,这个函数很简 单(如无特殊说明,代码以 1.2.1 为准): CODE: #include #include 龙兴平 (MSN: lxp8@sina.com) 第 5 页 共 83 页 2010-4-27
#include int daemon(nochdir, noclose) int nochdir, noclose; { int fd; switch (fork()) { case -1: return (-1); case 0: break; default: _exit(0); } if (setsid() == -1) return (-1); if (!nochdir) (void)chdir(”/”); if (!noclose && (fd = open(”/dev/null”, O_RDWR, 0)) != -1) { (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd > STDERR_FILENO) (void)close(fd); } return (0); } 这个函数 fork 了整个进程之后,父进程就退出,接着重新定位 STDIN 、 STDOUT 、 STDERR 到空设备, daemon 就建立成功了。 Memcached 本身的启动过程,在 memcached.c 的 main 函数中顺序如下: 1 、调用 settings_init() 设定初始化参数 2 、从启动命令中读取参数来设置 setting 值 3 、设定 LIMIT 参数 4 、开始网络 socket 监听(如果非 socketpath 存在)( 1.2 之后支持 UDP 方式) 5 、检查用户身份( Memcached 不允许 root 身份启动) 6 、如果有 socketpath 存在,开启 UNIX 本地连接(Sock 管道) 7 、如果以 -d 方式启动,创建守护进程(如上调用 daemon 函数) 龙兴平 (MSN: lxp8@sina.com) 第 6 页 共 83 页 2010-4-27
8 、初始化 item 、 event 、状态信息、 hash 、连接、 slab 9 、如设置中 managed 生效,创建 bucket 数组 10 、检查是否需要锁定内存页 11 、初始化信号、连接、删除队列 12 、如果 daemon 方式,处理进程 ID 13 、event 开始,启动过程结束, main 函数进入循环。 在 daemon 方式中,因为 stderr 已经被定向到黑洞,所以不会反馈执行中的可见错误信息。 memcached.c 的主循环函数是 drive_machine ,传入参数是指向当前的连接的结构指针,根 据 state 成员的状态来决定动作。 Memcached 使 用 一 套 自 定 义 的 协 议 完 成 数 据 交 换 , 它 的 protocol 文 档 可 以 参 考 : http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt 在 API 中,换行符号统一为\r\n ◎Memcached 的内存管理方式 Memcached 有一个很有特色的内存管理方式,为了提高效率,它使用预申请和分组的方式 管理内存空间,而并不是每次需要写入数据的时候去 malloc,删除数据的时候 free 一个指 针。Memcached 使用 slab->chunk 的组织方式管理内存。 1.1 和 1.2 的 slabs.c 中的 slab 空间划分算法有一些不同,后面会分别介绍。 Slab 可以理解为一个内存块,一个 slab 是 memcached 一次申请内存的最小单位,在 memcached 中,一个 slab 的大小默认为 1048576 字节(1MB),所以 memcached 都是整 MB 的使用内存。每一个 slab 被划分为若干个 chunk,每个 chunk 里保存一个 item,每个 item 同时包含了 item 结构体、key 和 value(注意在 memcached 中的 value 是只有字符串的)。slab 按照自己的 id 分别组成链表,这些链表又按 id 挂在一个 slabclass 数组上,整个结构看起来 有点像二维数组。slabclass 的长度在 1.1 中是 21,在 1.2 中是 200。 slab 有一个初始 chunk 大小,1.1 中是 1 字节,1.2 中是 80 字节,1.2 中有一个 factor 值,默 认为 1.25 在 1.1 中,chunk 大小表示为初始大小*2^n,n 为 classid,即:id 为 0 的 slab,每 chunk 大 小 1 字节,id 为 1 的 slab,每 chunk 大小 2 字节,id 为 2 的 slab,每 chunk 大小 4 字节…… id 为 20 的 slab,每 chunk 大小为 1MB,就是说 id 为 20 的 slab 里只有一个 chunk: CODE: void slabs_init(size_t limit) { int i; int size=1; 龙兴平 (MSN: lxp8@sina.com) 第 7 页 共 83 页 2010-4-27
mem_limit = limit; for(i=0; i<=POWER_LARGEST; i++, size*=2) { slabclass[i].size = size; slabclass[i].perslab = POWER_BLOCK / size; slabclass[i].slots = 0; slabclass[i].sl_curr = slabclass[i].sl_total = slabclass[i].slabs = 0; slabclass[i].end_page_ptr = 0; slabclass[i].end_page_free = 0; slabclass[i].slab_list = 0; slabclass[i].list_size = 0; slabclass[i].killing = 0; } /* for the test suite: faking of how much we’ve already malloc’d */ { char *t_initial_malloc = getenv(”T_MEMD_INITIAL_MALLOC”); if (t_initial_malloc) { mem_malloced = atol(getenv(”T_MEMD_INITIAL_MALLOC”)); } } /* pre-allocate slabs by default, unless the environment variable for testing is set to something non-zero */ { char *pre_alloc = getenv(”T_MEMD_SLABS_ALLOC”); if (!pre_alloc || atoi(pre_alloc)) { slabs_preallocate(limit / POWER_BLOCK); } } } 在 1.2 中,chunk 大小表示为初始大小*f^n,f 为 factor,在 memcached.c 中定义,n 为 classid, 同时,201 个头不是全部都要初始化的,因为 factor 可变,初始化只循环到计算出的大小达 到 slab 大小的一半为止,而且它是从 id1 开始的,即:id 为 1 的 slab,每 chunk 大小 80 字 节,id 为 2 的 slab,每 chunk 大小 80*f,id 为 3 的 slab,每 chunk 大小 80*f^2,初始化大小 有 一 个 修 正 值 CHUNK_ALIGN_BYTES , 用 来 保 证 n-byte 排 列 ( 保 证 结 果 是 CHUNK_ALIGN_BYTES 的整倍数)。这样,在标准情况下,memcached1.2 会初始化到 id40, 这个 slab 中每个 chunk 大小为 504692,每个 slab 中有两个 chunk。最后,slab_init 函数会在 最后补足一个 id41,它是整块的,也就是这个 slab 中只有一个 1MB 大的 chunk: CODE: void slabs_init(size_t limit, double factor) { int i = POWER_SMALLEST – 1; unsigned int size = sizeof(item) + settings.chunk_size; 龙兴平 (MSN: lxp8@sina.com) 第 8 页 共 83 页 2010-4-27
分享到:
收藏