分布式设计《初尝memcached》

之前听说过高性能的分布式缓存开源工具,但一直没有真正接触过,现在接触的产品中有用到过分布式缓存,所以决定一探究竟。memcached是一个优秀的开源的分布式缓存工具,也是目前比较火热的分布式缓存的解决方案雏形。memcached的服务端产品本身功能简洁,简单易用,但是玩法多种多样。但是事实上它是一个“伪分布式”解决方案,它本身并没有实现服务端分布式(服务端的memcached
server之间是不能通信的),所谓的分布式都是依靠客户端来实现,而目前市面上提供了客户端分布式实现的开源工具很多,在这里我主要以Spymemcached这个客户端实现为基础讲述一些memcached的原理和应用。

【原理说明】

之前提到了,memcached产品本身并未实现分布式,所以借助下列两幅网上流行的图片便可以直观的了解memcached的原理以及怎么玩分布式的。

1、存储(set)

假设memcached server有node1/node2/node3三个节点,现在应用程序需要存储"tokyo"/"test"这样一对键值对。memcached客户端接收应用程序传来的键值对"tokyo"/"test",通过算法(文章尾部会介绍具体的算法细节)从服务器列表中选中了node1作为目标存储服务器,接着发送set指令命令node1执行存储任务。

图1 存储数据

2、获取(get)

假设应用程序现在需要获取键"tokyo"对应的值数据"test",memcached客户端程序接收参数"tokyo",通过同样的算法从服务器列表中选中node1,接着发送get指令命令node1获取键"tokyo"对应的值数据。

图2  获取数据

【使用场景】

在网上也看过一些前辈描述过一些关于使用场景的描述,我简单总结下大致就以下两点

1、从memcached设计初衷的角度来看,memcached可以减少网站数据库的开销。对于经常需要读取,而又不经常改变的数据完全可以放到memcached中。

2、分布式应用之间共享数据。举个例子,登陆系统和商品查询系统是独立部署,并且是集群的。用户登陆了登陆系统之后如何将登录信息与其他的业务系统(商品查询系统)共享信息,这时就可以使用memcached缓存登录信息,商品查询系统便可以从memcached中获取用户的登陆信息。

【客户端源码分析】

1、客户端调用

以Spymemcached客户端实现为例,下面贴一段客户端的简单应用代码。下载链接memcached client

package com.lvmama.memcached;
import java.net.InetSocketAddress;
import net.spy.memcached.MemcachedClient;
public class TestSpymemcached {
 public static void main(String[] args) throws Exception{
  MemcachedClient client = new MemcachedClient(new InetSocketAddress("127.0.0.1", 11211)); //创建连接
  client.set("name", 10, "tony"); //set数据
  Object name = client.get("name"); //get数据
  System.out.println("name:" + name);
 }
}

2、余数hash算法

实现类为ArrayModNodeLocator,将传入的参数k(键),通过hash算法得出一个整数值,计算下memcached server的个数。拿着参数k的hash值对服务器节点的个数求余数。余数便是选中的服务器节点。

  public MemcachedNode getPrimary(String k) {
    return nodes[getServerForKey(k)];
  }

  private int getServerForKey(String key) {
    int rv = (int) (hashAlg.hash(key) % nodes.length);
    assert rv >= 0 : "Returned negative key for key " + key;
    assert rv < nodes.length : "Invalid server number " + rv + " for key "
        + key;
    return rv;
  }

3、consistent hash算法

算法原理见下图

第一步:将memcached服务器节点的hash值映射到一个0~2的32次方的环形数据结构上,存储方式为k(hash值),v(服务器节点);

第二步:将要保存的参数k计算hash值,并映射到环形数据结构中;

第三步:从参数k的hash值顺时针查找已映射的hash值,第一个hash值对应的服务器节点便是选中的目标存储服务器。

图 3   consistent hash算法

实现类为KetamaNodeLocator,代码结构比较简单:将要保存的k计算hash值作为参数传入getNodeForKey()方法,从hash值顺时针查找到剩余的环形数据结构tailMap,如果tailMap不为空则取tailMap中第一个已经映射hash值,如果tailMap为空则取整个环形数据结构ketamaNodes的第一个已经映射的hash值(从0开始),取得hash值便可以找到对应的memcached服务器节点。

  public MemcachedNode getPrimary(final String k) {
    MemcachedNode rv = getNodeForKey(hashAlg.hash(k));
    assert rv != null : "Found no node for key " + k;
    return rv;
  }

  MemcachedNode getNodeForKey(long hash) {
    final MemcachedNode rv;
    if (!ketamaNodes.containsKey(hash)) {
      // Java 1.6 adds a ceilingKey method, but I'm still stuck in 1.5
      // in a lot of places, so I'm doing this myself.
      SortedMap<Long, MemcachedNode> tailMap = getKetamaNodes().tailMap(hash);
      if (tailMap.isEmpty()) {
        hash = getKetamaNodes().firstKey();
      } else {
        hash = tailMap.firstKey();
      }
    }
    rv = getKetamaNodes().get(hash);
    return rv;
  }

4、算法优劣比较

余数hash算法:当服务器节点存在增加或者减少时,get数据时求得的余数同set数据时求得的余数很可能就不是同一个值,这时便大大降低了缓存读取的命中率。

consistent hash算法:当服务器节点存在增加或者减少时,如图3增加了node5,只有node2~node5之间的hash值会受到影响,由原来存储时命中的node4变成获取数据时命中的node5,其余hash值都不会受到影响。所以命中率较高。

而且有些consistent
hash算法的实现采用了虚拟节点的思想,使用一般的hash函数会使得服务器节点的映射分布的不均匀,因此可以为每个物理服务器节点分配100~200虚拟映射点,这样便可最大限度的减少节点分布不均的情况发生。

初尝memcached,如有描述有误,欢迎拍砖哈!后续会写memcached服务端的内存模型,内存管理,源码分析等文档,欢迎大家一起探讨!

时间: 2024-09-29 06:12:43

分布式设计《初尝memcached》的相关文章

.NET领域驱动设计—初尝(一:疑问、模式、原则、工具、过程、框架、实践)

.NET领域驱动设计—初尝(一:疑问.模式.原则.工具.过程.框架.实践) 2013-04-07 17:35:27 标签:.NET DDD 驱动设计 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://wangqingpei557.blog.51cto.com/1009349/1173006 1.1.疑问 1.1.1.UML何用 1.1.2.领域建模 1.2.模式 1.3.原则 1.4.工具 1.5.过程 1.6.框架 1.7.项

.NET领域驱动设计—初尝(原则、工具、过程、框架)

阅读目录: 1.原则 1.1.精简聚合 1.2.分离用例与接口功能(设计模式的用武之地) 2.工具.框架.组件 3.过程 1]原则 原则对于任何一项技术实现来说都是至关重要的,在设计某一个系统功能的时候我们讲究的是设计原则: [单一职责原则Single Responsibility Principle.里氏替换原则Liskov Substitution Principle.依赖倒置原则Dependence Inversion Principle.接口隔离原则Interface Segregati

.NET领域驱动设计—初尝(疑问、模式、原则、工具、过程、框架、实践)

阅读目录: 1.1.疑问 1.1.1.UML何用 1.1.2.领域建模 1.2.模式 1.3.原则 1.5.过程 1.6.框架 1.7.项目演示 最近在研究DDD颇有收获,所以整理出来跟大家分享,共同进步! 我们在设计业务系统的时候都会存在一个非常棘手而又无法回避的问题"业务扩展性"."业务灵活性."面向对象化",尽管我们熟练掌握设计思想.设计模式.设计原则等等关于如何设计灵活性的系统设计理论,但是我们似乎都没有将它们运用到真正业务系统设计.开发当中去,为

Challenge Checkio(python)—初尝python练习网站

最近在找点python语言练习的网站,发现这个网站不错 http://www.checkio.org/ 页面设计的也比较漂亮,比较适合学习python的语法知识.不过注册这个网站 开始就得解决一个python问题,不过很简单. 1 #python3.3 is inside 2 def checkio(els): 3 return els 4 5 if checkio([1, 2, 3, 4, 5, 6]) == 6: 6 print('Done!') 对上面的代码 修改checkio中的函数 函

初尝 Perl

本文将阐述以下几方面内容: 1.什么是Perl 2.Perl有什么用 3.Windows 下的Perl环境搭建 4.Perl 版Hello World 5.Perl 语法梗概 6.一些参考资料 什么是Perl Perl 是一门由 Larry Wall(拉里·沃尔)设计并实现的一门脚本语言,Larry Wall设计这门语言的最初的目的为了让UNIX 上得报表处理工作变得更方便.Perl 第一版的发行时间为 1987年(Python第一版的发行时间为 1991年.Ruby第一版的发行时间为 1995

初尝Mcafee之在ePO中进行策略和客户端任务设置【06】

一.策略和客户端任务概述 在ePO中点击"菜单",可以看到一个策略的大分类:ePO就是通过分配策略和客户端任务给客户端代理,然后代理将这些策略和客户端任务分配给本地相应的Mcafee杀毒防护软件进行执行: 策略是针对软件的内在参数和计划任务的配置,例如VirusScan是否扫描压缩文件,VirusScan的扫描计划的设置: 客户端任务是针对软件的外在交互,例如安装,部署,更新,信息统计等: 二.策略和客户端任务的分配结构: 策略和客户端任务的分配结构有点跟Windows Server的

《iOS应用逆向工程》学习笔记(五)初尝越狱插件OpenSSH

首先在越狱机子上装上OpenSSH插件,然后查看设备的IP地址,这里假设为192.168.xxx.xxx. 然后用Mac上的Terminal通过Open SSH连接到设备上(初次登录密码是alpine,必须立即修改,否则任何人都可以连接到你的机子上搞破坏). 连接命令为:ssh [email protected]设备IP地址 修改密码命令为:passwd 例如: $ ssh [email protected] The authenticity of host '192.168.xxx.xxx (

初尝backbone

backbone的基础知识在此将不再进行介绍.自己后续应该会整理出来,不过今天先把这几天学的成果用一个demo进行展示. 后续可运行demo将会在sinaapp上分享,不过近期在整理sinaapp上demo分享版块的重构,恕不能及时更新上去. 手把手教你搭建Hello World 虽然这次的开篇程序叫做helloworld有点牵强,但是我还是喜欢叫它为hello world~^_^ 以下程序改编自著名的todos程序,todos是什么?其实如果学过backbone的,肯定看到过todos的实例,

分布式进阶(十七)分布式设计介绍

分布式设计介绍 前言 分布式设计与开发在IDF05(Intel Developer Forum 2005)上,Intel首席执行官Craig Barrett就取消4GHz芯片计划一事,半开玩笑当众单膝下跪致歉,给广大软件开发者一个明显的信号,单纯依靠垂直提升硬件性能来提高系统性能的时代已结束,分布式开发的时代实际上早已悄悄地成为了时代的主流,吵得很热的云计算实际上只是包装在分布式之外的商业概念,很多开发者(包括我)都想加入研究云计算这个潮流,在google上通过"云计算"这个关键词来查