【软件工程实践】Pig项目5-Data目录源码分析-Bag

2021SC@SDUSC

 前面三篇博客,我们从创建Tuple的源码出发,逐层分析了构建Tuple的全过程,并对源码进行了解析。我们回忆博客《Pig项目2-Data目录源码分析》讲到的关于Pig的数据模型的内容,字段(field)的集合组成元组(tuple),元组的集合组成包(bag)。本篇我们将分析bag相关源码。

 相关资料链接:【Pig源码分析】谈谈Pig的数据模型 -数据库-火龙果软件工程

Data目录下包含bag的文件列表如下:

从上次分析tuple代码推断,创建bag大概率是调用BagFactory,那么如何查找调用语句呢?我这里使用了Ideal的全局查找,看到这里是通过get.Instance()方法创建的

结合参考资料,发现除了这种创建方法,还有另外的一种创建方法

其中,前一种方法是DefaultBagFactory的默认实现,另外一种是使用NonSpillableDataBag

再搞清楚两者有何区别之前,我们先梳理一下DefaultFactory的结构,类图如下

 可以看出,使用第二种方式直接创建bag实际上是直接跳过了DefaultBagFactory,直接调用,而NonSpillableDataBag与其他的Bag类实现最大的区别就是它不是继承自DefaultAbstractBag,这么做理由也可以理解,就是实际上NonSpillableDataBag因为不符合DefaultAbstractBag所以不使用DefaultBagFactory调用

于是本篇先从简单的NonSpillableDataBag开始分析Bag的结构

 以下为对DataBag的源码分析

 

/**
 *
元组的集合。一个数据包可能适合也可能不适合(fit to)内存.
 * DataBag
扩展了spillable,这意味着它(registers with)注册了一个内存管理器(memory resister)
 *
默认情况下,它尝试将所有内容保存在内存中.
 *
如果内存管理器请求溢出到磁盘(通过调用spill()),它获取内存中的所有内容,打开一个spill文件然后把内容写出来。这种情况可能会发生多次。这个袋子跟踪所有溢出的文件
 * DataBag提供了一个Iterator接口,允许调用者读取内容。迭代器知道数据溢出。

*他们必须能够处理从文件读取,因为他们从内存中读取的数据可能已经溢出到磁盘下面。
 * DataBag接口假设所有数据都是先写后读的。即DataBag不能作为队列使用。如果数据在读取后写入,则结果是未定义的。由于速度的原因,在每次添加或读取时不会检查此条件。
 * 由于溢出是异步的(请求溢出的内存管理器在单独的线程中运行),所有处理mContents Collection(包中包含的元组集合)的操作都必须是同步的。这意味着从DataBag读取数据目前是序列化的。目前这是可以的,因为猪的执行目前是单线程的。我们尝试了ReadWriteLock,但是发现它比使用synchronize关键字慢10倍。

如果pig将其执行模型更改为多线程,我们可能需要回到这个问题,因为同步读取很可能会破坏多线程执行的目的。
  数据包(data bag)有几种类型,默认的、排序的和不同的。类型必须预先选择,没有办法在飞行中转换一个包。

默认的数据包不保证检索元组的任何特定顺序,并且可能包含重复的元组。已排序的数据包保证了元组将按顺序检索,其中“in order”Tuple的默认比较器定义,或由创建数据包时调用者提供的比较器定义。分类后的袋子可能含有重复的东西。不同的包(Distinct bags)不能保证任何特定的检索顺序,但可以保证它们不会包含重复的元组
 */

@InterfaceAudience.Public
@InterfaceStability.Stable

public interface DataBag extends Spillable, WritableComparable, Iterable<Tuple>, Serializable {
   
/**
     *
获取包中元素的数量,包括内存和磁盘上的元素。
     * @return number of elements in the bag
     */
   
long size();

   
/**
     *
看看袋子是否分类了。
     * @return true if this is a sorted data bag, false otherwise.
     */
   
boolean isSorted();
   
   
/**
     *
看看袋子是否 distinct.
     * @return true if the bag is a distinct bag, false otherwise.
     */
   
boolean isDistinct();
   
   
/**
     *
获取包的迭代器。对于默认的和不同的包,不保证有特定的顺序。对于已分类的袋子,保证按照所提供的比较器进行排序。
     * @return tuple iterator
     */
   
Iterator<Tuple> iterator();

   
/**
     *
向包中添加一个元组。
     * @param t tuple to add.
     */
   
void add(Tuple t);

   
/**
     *
把袋子里的东西加到袋子里
     * @param b bag to add contents of.
     */
   
void addAll(DataBag b);

   
/**
     *
清除包中的内容,包括磁盘和内存中的内容。调用此函数后,任何读取的尝试都会产生未定义的结果.
     */
   
void clear();

   
/**
     *
返回包的哈希码值。袋子的哈希码定义为每个元组哈希码的和。这确保b1.equals(b2)意味着b1.hashCode() == b2. hashcode ()
     *
     * @return the hash code value for this bag
     */
   
int hashCode();

   
/** FuncEvalSpec.FakeDataBag使用这个。
     *
     * @param stale Set stale state.
     */
   
@InterfaceAudience.Private
   
void markStale(boolean stale);
}

 接口内容可以总结如下

这里有意义的部分大概就是接口前那段注释了,我们知道了bag有spillable的特性,即内存溢出时会写入磁盘,以及pig实际上是一个单线程的执行模型

 接下来我们来看看继承自databag的NonSpillableDataBag,从名字就知道,它是个非溢出的databag因此这也是一种不好的继承,更好的做法是将SpillableDataBag分离,即NonSpillableDataBag、SpillableDataBag继承Databag

接下来我们来看看NonSpillableDataBag的源码

/**
 *
具有多个元组(可能)的无序集合。元组存储在数组列表中,因为不关心顺序或区别。隐含的假设是,该类的用户只存储那些能够容纳在内存中的元组——不会将这个包溢出到磁盘。 */

public class NonSpillableDataBag implements DataBag {
   
// 这个类不扩展DefaultAbstractBag的原因是,我们不想用它不需要的成员来膨胀这个类(DefaultAbstractBag有很多与溢出相关的成员,这里不需要)
   
private static final long serialVersionUID = 1L;

   
    private List<Tuple> mContents;   

   
public NonSpillableDataBag() {
        mContents =
new ArrayList<Tuple>();
    }

   
/**
     *
如果你事先知道要在这个包中放入多少元组,就使用这个构造函数.
     * @param tupleCount
     */
   
public NonSpillableDataBag(int tupleCount){
        mContents =
new ArrayList<Tuple>(tupleCount);
    }
   
   
/**
     *
这个构造函数通过获得列表的所有权而不是复制列表的内容,从现有的元组列表中创建一个包。
     * @param listOfTuples List<Tuple> containing the tuples
     */
   
public NonSpillableDataBag(List<Tuple> listOfTuples) {
        mContents = listOfTuples;
    }

   
public boolean isSorted() {
       
return false;
    }
   
   
public boolean isDistinct() {
       
return false;
    }
   
   
public Iterator<Tuple> iterator() {
       
return new NonSpillableDataBagIterator();
    }

   
/**
     *
处理从包中获取下一个元组的迭代器
     */
   
private class NonSpillableDataBagIterator implements Iterator<Tuple> {

       
private int mCntr = 0;

       
public boolean hasNext() {
           
return (mCntr < mContents.size());
        }

       
public Tuple next() {
           
// 这将每1024次报告进展到下一次.
            //
这应该比使用mod快得多
           
if ((mCntr & 0x3ff) == 0) reportProgress();

           
return mContents.get(mCntr++);
        }

       
/**
         *
不可执行
         */
       
public void remove() { throw new RuntimeException("Cannot remove() from NonSpillableDataBag.iterator()");}
    }   

   
/**
     *
HDFS报告进度
     */
   
protected void reportProgress() {
       
if (PhysicalOperator.getReporter() != null) {
            PhysicalOperator.getReporter().progress();
        }
    }

    @Override
   
public void add(Tuple t) {
        mContents.add(t);
    }

    @Override
   
public void addAll(DataBag b) {
       
for (Tuple t : b) {
            mContents.add(t);
        }
    }

    @Override
   
public void clear() {
        mContents.clear();       
    }

    @Override
   
public void markStale(boolean stale) {
       
throw new RuntimeException("NonSpillableDataBag cannot be marked stale");
    }

    @Override
   
public long size() {
       
return mContents.size();
    }

    @Override
   
public long getMemorySize() {
       
return 0;
    }

    @Override
   
public long spill() {
       
// TODO Auto-generated method stub
       
return 0;
    }

   
/**
     *
把包里的东西写到磁盘上。
     * @param out DataOutput to write data to.
     * @throws IOException (passes it on from underlying calls).
     */
   
public void write(DataOutput out) throws IOException {
       
// 我们不关心这个包是否已排序或不同,因为使用迭代器来编写它将确保这些东西正确地出现。在另一端,没有理由浪费时间重新排序或重新应用distinct.
       
out.writeLong(size());
        Iterator<Tuple> it = iterator();
       
while (it.hasNext()) {
            Tuple item = it.next();
            item.write(out);
        }   
    }
 
   
/**
     *
从磁盘读取一个包
     * @param in DataInput to read data from.
     * @throws IOException (passes it on from underlying calls).
     */
   
public void readFields(DataInput in) throws IOException {
       
long size = in.readLong();
       
       
for (long i = 0; i < size; i++) {
           
try {
                Object o = DataReaderWriter.readDatum(in);
                add((Tuple)o);
            }
catch (ExecException ee) {
               
throw ee;
            }
        }
    }
   
   
/* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
   
@Override
   
public boolean equals(Object obj) {
       
if( obj == null) {
           
return false;
        }
       
return compareTo(obj) == 0;
    }

   
public int hashCode() {
       
int hash = 0;
       
for( Tuple t : mContents ) {
            hash += t.hashCode();
        }
       
return hash;
    }

    @SuppressWarnings(
"unchecked")
    @Override
   
public int compareTo(Object other) {
       
if (this == other)
           
return 0;
       
if (other instanceof DataBag) {
            DataBag bOther = (DataBag) other;
           
if (this.size() != bOther.size()) {
               
if (this.size() > bOther.size()) return 1;
               
else return -1;
            }

           
// 这是假的。但我必须知道两个包是否有相同的元组,不管顺序如何。希望在大多数情况下,上面的大小检查可以防止这种情况发生。如果任何一个包还没有分类,创建一个已分类的包,这样我可以保证秩序。         
           
BagFactory factory = BagFactory.getInstance();
           
            DataBag thisClone;
            DataBag otherClone;
            thisClone = factory.newSortedBag(
null);
            Iterator<Tuple> i = iterator();
           
while (i.hasNext()) thisClone.add(i.next());
           
if (((DataBag) other).isSorted() || ((DataBag) other).isDistinct()) {
                otherClone = bOther;
            }
else {
                otherClone = factory.newSortedBag(
null);
                i = bOther.iterator();
               
while (i.hasNext()) otherClone.add(i.next());
            }
            Iterator<Tuple> thisIt = thisClone.iterator();
            Iterator<Tuple> otherIt = otherClone.iterator();
           
while (thisIt.hasNext() && otherIt.hasNext()) {
                Tuple thisT = thisIt.next();
                Tuple otherT = otherIt.next();
               
               
int c = thisT.compareTo(otherT);
               
if (c != 0) return c;
            }
           
           
return 0;   // 如果我们走了这么远,它们一定是相等的
       
} else {
           
return DataType.compare(this, other);
        }
    }
   
   
/**
     * Write the bag into a string. */
   
@Override
   
public String toString() {
        StringBuffer sb =
new StringBuffer();
        sb.append(
'{');
        Iterator<Tuple> it = iterator();
       
while ( it.hasNext() ) {
            Tuple t = it.next();
            String s = t.toString();
            sb.append(s);
           
if (it.hasNext()) sb.append(",");
        }
        sb.append(
'}');
       
return sb.toString();
    }

 NonSpillableDataBag很容易,看了注释基本都能理解,那么本篇分析就到这里。下期我们将分析"Spillable"的三种DataBag

 

Guess you like

Origin blog.csdn.net/Aulic/article/details/121042302