首页 > 其他分享 >MIT6.830-Lab3

MIT6.830-Lab3

时间:2024-02-29 20:58:18浏览次数:19  
标签:join int t2 t1 Lab3 cost MIT6.830 连接

simpleDB项目地址

概览

image

  • IntHistogram作用
    1. 估计条件过滤某表的某个Int字段时,结果集记录数和该表记录总数的比例;
  • StringHistogram类似IntHistogram
  • TableStats作用
    1. 存放某表各个字段对应的直方图对象(IntHistogramStringHistogram类型);
    2. 使用静态对象ConcurrentMap<String, TableStats> statsMap存放数据库中各表的TableStats对象;
    3. 计算按某个字段的值过滤后,结果集记录数和该表记录总数的比例;
  • LogicalPlan作用
    1. 分别将查询语句的各部分包装成各类对象,以便后续处理;
    2. 将查询语句进行优化,使得消耗资源最低;
  • JoinOptimizer作用
    1. 将连接查询进行优化,返回最佳的连接顺序;
  • Parser作用
    1. 用于加载数据库表目录文件catalog.txt到内存中;
    2. 使用tableStatsMap成员变量生成数据库内各个表的字段的直方图(IntHistogramStringHistogram,用来估计查询或连接操作的消耗);
    3. 用于解析输入的查询语句,将其包装为LogicalPlan类对象(LogicalPlan类中的physicalPlan()方法可以返回优化后结果集的OpIterator类对象);
    4. 打印查询结果到控制台;

实验部分

实验1

为了简化计算,扫描损失和连接损失的公式如下(ntups代表表中记录数):

scancost(t1)=ntups(t1) x SCALING_FACTOR.
joincost(t1 join t2) = scancost(t1) + ntups(t1) x scancost(t2) //IO cost
                       + ntups(t1) x ntups(t2)  //CPU cost

此外,文档中建议使用直方图估计法简化含有过滤条件的损失计算,以对某个Int字段过滤为例,先获取表中该字段的最大值max和最小值min,然后将[min,max]这个区域划分为n个桶,之后再遍历一遍表,依照每条表记录中该字段的值,更新对应位置桶的大小,最终就获得了估算过滤损失的直方图。

  • IntHistogram
public double estimateSelectivity(Predicate.Op op, int v) {
        int idx = (int) ((v-min)/bucketWidth);
        switch (op){
            case EQUALS:
                if(v<min || v>max)
                    return 0.0;
                else {
                    double eqTuples = buckets[idx]*1.0/bucketWidth;
                    return eqTuples/totalTuples;
                }
            case GREATER_THAN:
                if(v<min)
                    return 1.0;
                else if(v>max)
                    return 0.0;
                else {
                    int right = (int) (min+(idx+1)*bucketWidth-1);
                    double sumGtTuples = (right-v)*1.0/bucketWidth*buckets[idx];
                    for(int j=idx+1;j<buckets.length;j++)
                        sumGtTuples+=buckets[j];
                    return sumGtTuples/totalTuples;
                }
            case LESS_THAN:
                if(v<min)
                    return 0.0;
                else if(v>max)
                    return 1.0;
                else {
                    int left = (int) (min+idx*bucketWidth);
                    double sumLtTuples = (v-left)*1.0/bucketWidth*buckets[idx];
                    for(int j=0;j<idx;j++)
                        sumLtTuples+=buckets[j];
                    return sumLtTuples/totalTuples;
                }
            case NOT_EQUALS:
                return 1.0 - estimateSelectivity(Predicate.Op.EQUALS,v);
            case GREATER_THAN_OR_EQ:
                return estimateSelectivity(Predicate.Op.GREATER_THAN,v-1);
            case LESS_THAN_OR_EQ:
                return estimateSelectivity(Predicate.Op.LESS_THAN,v+1);
            default:
                throw new RuntimeException("illegal operator");
        }
    }
  • StringHistogram
    它将每个String转为int值,借助内部的IntHistogram成员变量实现估算直方图。

实验2

  • TableStats
    使用Map<Integer,IntHistogram> intHistogramMapMap<Integer,StringHistogram> strHistogramMap来存储表中字段对应的直方图对象(key是字段序号),核心方法是构造方法,大致思路是:先获取每个整形字段的最大最小值,接着根据这些值初始化各整形字段对应的IntHistogram对象,最后再次遍历表,设置每个IntHistogram对象和StringHistogram对象的桶大小。
    public TableStats(int tableid, int ioCostPerPage) {
        this.ioCostPerPage = ioCostPerPage;
        DbFile databaseFile = Database.getCatalog().getDatabaseFile(tableid);
        TupleDesc tupleDesc = databaseFile.getTupleDesc();
        HashMap<Integer, Integer> max = new HashMap<>();
        HashMap<Integer, Integer> min = new HashMap<>();
        this.intHistogramMap = new HashMap<>();
        this.strHistogramMap = new HashMap<>();
        // 初始化整型字段的值
        for(int i=0;i<tupleDesc.numFields();i++){
            if(tupleDesc.getFieldType(i) == Type.INT_TYPE){
                max.put(i,Integer.MIN_VALUE);
                min.put(i,Integer.MAX_VALUE);
            }
        }
        // 获取各整型字段的最大最小值
        int tupleNum = 0;
        DbFileIterator iterator = databaseFile.iterator(tid);
        try {
            iterator.open();
            while (iterator.hasNext()){
                tupleNum++;
                Tuple next = iterator.next();
                for(int i=0;i<tupleDesc.numFields();i++){
                    if(tupleDesc.getFieldType(i) == Type.INT_TYPE){
                        IntField intField = (IntField) next.getField(i);
                        if(intField.getValue() > max.get(i)){
                            max.put(i,intField.getValue());
                        }
                        if(intField.getValue() < min.get(i)){
                            min.put(i,intField.getValue());
                        }
                    }else if(tupleDesc.getFieldType(i) == Type.STRING_TYPE){
                        StringHistogram strHistogram = new StringHistogram(NUM_HIST_BINS);
                        this.strHistogramMap.put(i,strHistogram);
                    }
                }
            }
	// 初始化IntHistogram
            for(Integer integer: max.keySet()){
                IntHistogram intHistogram = new IntHistogram(NUM_HIST_BINS, min.get(integer), max.get(integer));
                this.intHistogramMap.put(integer,intHistogram);
            }
            this.tableTuples = tupleNum;
            iterator.rewind();
	// 设置桶大小
            while (iterator.hasNext()){
                Tuple next = iterator.next();
                for(int i=0;i<tupleDesc.numFields();i++){
                    if(tupleDesc.getFieldType(i) == Type.INT_TYPE){
                        IntField intField = (IntField) next.getField(i);
                        this.intHistogramMap.get(i).addValue(intField.getValue());
                    }else if(tupleDesc.getFieldType(i) == Type.STRING_TYPE){
                        StringField strField = (StringField) next.getField(i);
                        this.strHistogramMap.get(i).addValue(strField.getValue());
                    }
                }
            }
            if(databaseFile instanceof HeapFile)
                this.tablePages = ((HeapFile)databaseFile).numPages();
            else if(databaseFile instanceof BTreeFile)
                this.tablePages = ((BTreeFile)databaseFile).numPages();
            else{
                this.tablePages = 0;
                throw new RuntimeException("can not confirm the page");
            }


        } catch (DbException e) {
            throw new RuntimeException(e);
        } catch (TransactionAbortedException e) {
            throw new RuntimeException(e);
        }

    }

实验3

文档指出计算joincost((t1 join t2) join t3)这种多连接查询的花费时,首先需要估算(t1 join t2)操作返回的结果集数量,但这一操作较为复杂,本实验中只要求我们做个大概估算,估算时需要遵循以下三点:

  1. 对于等值连接t1 join t2 on t1.i=t2.j,当it1中的主键时,估算的结果不能大于t2j列非空的记录条数;
  2. 对于不存在主键的等值连接,可以直接将较大表的记录条数作为估算结果;
  3. 对于范围连接,可以直接将两表记录数的乘积*30%作为估算结果;

节点类

  • LogicalJoinNode:存放两表连接信息,如t1 join t2 on t1.i=t2.j
  • LogicalSubplanJoinNode:继承了LogicalJoinNode,存放多表连接信息,如(t1 join t2) join t3
  • LogicalScanNode:存放查询的表信息,如select * from t中的表t
  • LogicalFilterNode:存放where子句的过滤信息,如where t1.i = 1
  • LogicalSelectListNode:存放查询的字段信息,如select max(c) form t1中的max(c)
 public double estimateJoinCost(LogicalJoinNode j, int card1, int card2,
            double cost1, double cost2) {
        if (j instanceof LogicalSubplanJoinNode) {
            // lab3对子查询不要求,使用默认实现
            return card1 + cost1 + cost2;
        } else {
            double ioCost = cost1 + card1 * cost2;
            double cpuCost = card1 * card2;
            return ioCost + cpuCost;
        }
    }
public static int estimateTableJoinCardinality(Predicate.Op joinOp,
                                                   String table1Alias, String table2Alias, String field1PureName,
                                                   String field2PureName, int card1, int card2, boolean t1pkey,
                                                   boolean t2pkey, Map<String, TableStats> stats,
                                                   Map<String, Integer> tableAliasToId) {
        switch (joinOp){
            case EQUALS:
                if(t1pkey || t2pkey)
                    return Math.min(card1,card2);
                else
                    return Math.max(card1,card2);
            default:
                return (int) (card1*card2*0.3);
        }
    }

实验4

Selinger算法获取最佳连接顺序:

1. j = set of join nodes	//j是一组连接节点
2. for (i in 1...|j|):	//从1遍历到j数组的长度
3.     for s in {all length i subsets of j} //生成所有长度为i的j数组的子集,遍历这些子集
4.       bestPlan = {}	// 用来存储该子集的最佳连接顺序
5.       for s' in {all join nodes of s} //遍历该子集中的所有连接节点
6.            subplan = optjoin(s-s') //从缓存取出去除s'节点后的最优连接 subPlan
7.            plan = best way to join s' to subplan //计算将节点s'加入subPlan的最优连接 plan
8.            if (cost(plan) < cost(bestPlan)) //如果plan的消耗小于bestPlan
9.               bestPlan = plan //将plan赋值给bestPlan
10.      optjoin(s) = bestPlan //把连接节点集合s的最优连接,放入缓存
11. return optjoin(j)

代码实现为:

public List<LogicalJoinNode> orderJoins(
            Map<String, TableStats> stats,
            Map<String, Double> filterSelectivities, boolean explain)
            throws ParsingException {
        PlanCache planCache = new PlanCache();
        for(int i=1;i<=joins.size();i++){
            Set<Set<LogicalJoinNode>> subsets = enumerateSubsets(joins, i);
            for(Set<LogicalJoinNode> subset:subsets){
                CostCard bestCostCard = new CostCard();
                bestCostCard.cost = Double.MAX_VALUE;
                for(LogicalJoinNode node:subset){
                    CostCard costCard = computeCostAndCardOfSubplan(stats, filterSelectivities, node, subset, bestCostCard.cost, planCache);
                    if(costCard == null)
                        continue;
                    else if(costCard.cost < bestCostCard.cost){
                        bestCostCard = costCard;
                    }
                }
                if(bestCostCard.cost != Double.MAX_VALUE)
                    planCache.addPlan(subset,bestCostCard.cost,bestCostCard.card,bestCostCard.plan);
            }
        }
        if (explain)
            printJoins(joins,planCache,stats,filterSelectivities);
        return planCache.getOrder(new HashSet<>(joins));
    }

注意computeCostAndCardOfSubplan()方法实现了伪代码的6,7两行,重要参数含义分别是:

  • stats:存放所有表的TableStats集合,key值是表名,用来计算表的cost
  • filterSelectivities:存放所有表的过滤度(即过滤操作后结果集占总集合的比例)集合,key值是表别名,是由LogicalPlan解析查询语句的过程中取得的,用来计算表的card
  • pc:存放所有长度连接数组对应的最优连接集合的缓存,以及该最优连接集合的cost和card

该方法在内部会计算交换连接顺序后的花费(比如 t1 join t2换成t2 join t1),以选出最优的连接方式

CostCard computeCostAndCardOfSubplan(
            Map<String, TableStats> stats,
            Map<String, Double> filterSelectivities,
            LogicalJoinNode joinToRemove, Set<LogicalJoinNode> joinSet,
            double bestCostSoFar, PlanCache pc) throws ParsingException 

CostCard类存储一个join连接的数组,以及该数组进行连接所产生的cost和card

标签:join,int,t2,t1,Lab3,cost,MIT6.830,连接
From: https://www.cnblogs.com/rockdow/p/18045432

相关文章

  • MIT6.830-Lab2
    simpleDB项目地址实验部分实验1Predicate类用来存放对表记录进行条件过滤的信息(要过滤字段的序号,具体的比较规则,用来比较的字段),其内部的枚举类Op就是比较规则类,filter()方法的实现使用Field接口中的compare()即可。JoinPredicate类用来存放两表的记录进行连接的信息(两表......
  • MIT 6.S081入门lab3 页表
    MIT6.S081入门lab3页表一、参考资料阅读与总结1.xv6book书籍阅读(页表)a.总览页表:操作系统为每一个进程提供私有空间和内存的机制,使每一个进程有着自己的虚拟地址空间。本质上是基于分页的内存空间管理方法。页表存储:其实就是MMU,其存储了从虚拟地址VA到物理地址PA的映射......
  • MIT6.830-Lab1
    TupleDesc类TupleDesc类用来存储表结构,使用静态内部类TDItem封装字段类型和字段名称。Tuple类Tuple类用来存储具体的数据行,使用Filed接口数组存放不同字段类型的数据,使用TupleDesc成员变量存放与该数据行关联的表结构信息,使用RecordId成员变量存放该数据行的行号和所处的数......
  • mit6.828 - lab3练习笔记
    PartAExercise1练习1.修改`kern/pmap.c`中的`mem_init()`,分配并映射`envs`数组。该数组由`Env`结构的`NENV`实例组成,分配方式与分配页面数组类似。与页面数组一样,支持`envs`的内存也应在`UENVS`(定义于`inc/mlayout.h`)处映射为用户只读,这样用户进程才能读取该......
  • Lab3:数据处理基本方法及创新应用(基础)
    ++x是先进行x=x+1,再返回x;x++是先返回x,再进行x++55/7=7,因为是整型运算;55/7.0=7.85714286,因为是浮点型运算'b'<'a'返回值为1;x>y返回值在x>y时为1,x<=y为0x>0时返回x,否则返回-1x<<2==x*48=1000,9=1001,z=8&9=1000=866&&88返回值为1,是逻辑与;6......
  • Lab3 存储过程与触发器
              实验三存储过程与触发器实验目的:学习SQL语言进行编程的基本方法与技术,能够编写存储过程、触发器解决数据库需要处理的复杂问题。实验内容:1、 设计一个存储过程或者自定义函数,练习存储过程的设计方法。2、 设计触发器,理解触发器的工作原理与设计方法......
  • CS144-lab3
    Checkpoint3Writeup该lab主要实现TCP发送方,细节比较多,具有一定难度,编写时需要从整体上理清设计思路,然后再实现具体的函数。Timer由于要实现TCP中的超时重传功能,所以需要在发送方维护一个定时器,但不需要自己使用计时函数,因为文档里说明了所有对时间的了解都是通过tick函数得到......
  • 恶意代码分析 动态行为分析 Lab3-1 Lab3-2 Lab3-3 Lab3-4
    笔记动态分析基础,这部分还没涉及到看反汇编进行分析,主要是运行程序,然后通过监控软件检测程序运行的内容使用沙箱查看运行报告,可以获取一部分信息首先要在虚拟机上运行恶意代码:如果是DLL,可以通过rundll32.exeDLLName,ExportFun来进行执行如果是服务DLL,则需要运行其中导出的安装服......
  • lab3 page tables
    1.Speedupsystemcalls(easy)要求:有些操作系统(例如Linux)通过在用户空间和内核之间的只读区域共享数据来加速某些系统调用。这样可以消除在执行这些系统调用时进行内核交叉的需要(以优化用户模式到内核模式的陷阱机制,对于某些系统调用不再需要切换模式)。第一个任务是为xv6中......
  • 【哈佛cs50 2022】lab3 & problemSet3【ing】
    (1)lab3如何测试每个代码运行所需要的时间?time./sort1sorted5000.txt sort1sort2sort3sorted5000.txt0.037s0.020s0.045ssorted10000.txt0.058s0.050s0.151ssorted50000.txt1.244s2.238s5.637sreversed5000.txt0.088s0.026s0.045srever......