首页 > 数据库 > cassandra 学习笔记(3)

cassandra 学习笔记(3)

2010年3月15日
253 views 评论 发表评论

当某个end point拿到一个key(比如”王老六”)并想取出他的相关信息的时候,这个节点是怎么知道这个key的相关信息是存放在哪些节点中的呢?
以下将用从客户端拿到的”get_clomun”请求为例,进行说明:

      “get_column”的相关信息会在CassandraServer的get_column(String tablename, String key, String columnPath)方法中被封装成一个readCommand实例,该对象简单包含了请求信息,另外也提供了一些别的方法。

      然后该command实例会最终被交给 StorageProxy.readProtocol(command,StorageService.ConsistencyLevel.WEAK)来处理,并期待这个方法能返回一个和key关联的columnFamily的数据,这些数据被装在一个row对象里返回的。

      在上述的StorageProxy.readProtocol(…)这个方法中,首先要做的事情就是根据提交的key(“王老六”)确定他的相关信息内容是存放在那些end point中的。而这个寻找end point的工作由StorageService.instance().getNStorageEndPoint(command.key)来完成。

      实际上,上述的StorageService的getNStorageEndPoint(String key)方法调用了他持有的实例”nodePicker_”的getStorageEndPoints(Token token) 来完成寻找end point这件事情。

      题外话:StorageService这个实例提供了本地节点数据存储和与其他节点交互的服务,这个实例是在节点启动之初被建立的,在建立的时候该实例会初始化一个nodePicker_放在其内部,这个nodePicker主要负责本地节点同其他节点的交互规则,也就是如何选择其他节点的策略。当前的策略有两种:RackAwareStrategy–机架敏感 和 RackUnawareStrategy–非机架敏感,默认策略是RackUnawareStrategy。

      所以默认情况下,nodePicker_就是一个RackUnawareStrategy实例,而他的 getStorageEndPoints(Token token)将被用来查找合适我们给出的key(“王老六”)的end point,这里合适的意思是“存有关于这个key的信息”。而这里提到的token是由key封装得来的,他对key进行了hash,并且继承了 Comparable接口。也就是说,这个token实例是可以和其他同类token比较大小的,而这样做的目的是为了“可以在Token组成的list 上进行二分查询Collection.binarySort,并定位查找目标的在list中的相对位置”–这种查找操作将被经常使用。默认情况下 token是一个BigIntegerToken的实例。

      nodePicker_最终会将查找的工作交给getStorageEndPoints(Token token, Map<Token, EndPoint> tokenToEndPointMap)来做,这个方法会返回一个查找到的end_point的数组。以下是对RackUnawareStrategy中的相应方法的说明(因为默认是使用这个方法):
//此处的tokenToEndPointMap保存了该节点知道的所有其他节点,并且可以根据他们的token查找到

//此处的token是我们要查找的key(“王老五”)生成的token,他跟上面end point的token生成的算法是相同的,也都是BigIntegerToken

  1.  
  2. public EndPoint[] getStorageEndPoints(Token token, Map<Token, EndPoint> tokenToEndPointMap)
  3.     {
  4.         //根据key寻址的切入点
  5.         int startIndex;
  6.         List<EndPoint> list = new ArrayList<EndPoint>();
  7.         int foundCount = 0;
  8.         //将key的set装入一个ArrayList中,并完成排序,(一位token是comparable的)
  9.         List tokens = new ArrayList<Token>(tokenToEndPointMap.keySet());
  10.         Collections.sort(tokens);
  11.         //在排好序的tokens中二叉树搜索token,并返回token所在的位置的index
  12.  
  13.         //个人认为:根本别指望tokens里面能有我们要找的token,因为一个是end point的token集合,一个是查找key("王老六")生成的 
  14.  
  15.         //token,生成的时候都使用了hash,并且加入了大随即数,重合的概率很低。
  16.  
  17.         //这样做的主要目的是为了获得下面这个index,(参见binarySearch的说明可知)。
  18.  
  19.         //也就是:虽然下面这条语句不大可能从tokens中取出东西,但是生成的index将告诉我们,哪个end point的token离我们给出的
  20.  
  21.         //key"王老六"的token“最近”。而且,在节点情况固定下来的情况下,用当前这种方法,
  22.  
  23.         //key"王老六"的token确定的对应的end point也是不会改变的(每次都会取到这个end point,除非end point本身注册的变了)。
  24.  
  25.         //也就是说:这就成了一种方法,一种能够根据数据key("王老六")定位到一个end point的方法,
  26.  
  27.         //如果写数据时使用了这种策略找到一个end point 节点然后写进数据,那么手持相应的key在读数据的时候,同样使用这个策略,也
  28.  
  29.         //相应的能找到当时存入数据的那个节点,并把数据读出来。
  30.           int index = Collections.binarySearch(tokens, token);
  31.         //基本上这个条件里面的语句肯定会被执行
  32.  
  33.         //那如果真的用key("王老六")从tokens中找到对应了呢?这个方法里的策略就是,那就直接拿这个被确定的节点来当 “邻居”节点了
  34.  
  35.         //而key的tokens和end_point的token之所以能混在一起说话,是因为他们都是BigIntegerToken,在同一个数据空间中
  36.         if(index < 0)
  37.         {
  38.  
  39.             //以下的运算将把key("王老六")的"邻居"节点给"翻译"出来
  40.             index = (index + 1) * (-1);//得到可以插入的位置
  41.             if (index >= tokens.size())
  42.                 index = 0;
  43.         }
  44.         int totalNodes = tokens.size();
  45.         // Add the node at the index by default
  46.  
  47.         //把我们找到的"邻居"节点放到一个要返回的list中,当然,对于一个key("王老六")的信息还可能在其他节点上存有副本(replicas)
  48.  
  49.         //再下面的操作会将这些存有副本的节点也一同取出
  50.         list.add(tokenToEndPointMap.get(tokens.get(index)));
  51.         foundCount++;
  52.         //本来index是从0…一直到size()的一条线,如果我们用如下的方式选取index,就好似将所有的index数值放在了一个圆圈上,然后按
  53.  
  54.         //从小到大的方向在选取下一个index。startIndex就是一个切入点,本质上Partitioner提供了这个可被"切入"的结构和线索
  55.  
  56.         //猜想这也是为什么叫Partitioner的原因吧
  57.         startIndex = (index + 1)%totalNodes;
  58.         // If we found N number of nodes we are good. This loop will just exit. Otherwise just
  59.         // loop through the list and add until we have N nodes.
  60.         // replicas_对应storage-conf.xml中的最大副本数量
  61.    
  62.  
  63.         //将存有副本的节点也一同取出,就是最初"邻居"节点往后的若干个节点,具体若干几个是在storage-conf.xml的
  64.  
  65.         //<ReplicationFactor>标签中定义的
  66.         for (int i = startIndex, count = 1; count < totalNodes &amp;&amp; foundCount < replicas_; ++count, i = (i+1)%totalNodes)
  67.         {
  68.             if( ! list.contains(tokenToEndPointMap.get(tokens.get(i))))
  69.             {
  70.                 list.add(tokenToEndPointMap.get(tokens.get(i)));
  71.                 foundCount++;
  72.             }
  73.         }
  74.  
  75.         //统一下节点监听端口的信息
  76.         retrofitPorts(list);
  77.         return list.toArray(new EndPoint[list.size()]);
  78.     }
  79.  

转:http://blog.csdn.net/pakly_9527/archive/2009/08/14/4446665.aspx

纯净水 数据库 , , ,

  1. 目前还没有任何评论.
  1. 目前还没有任何 trackbacks 和 pingbacks.
  • 粤ICP备09032914号