Designing Data-Intensive Applications 简读摘要——第二章数据模型和查询语言(part2)

数据查询语言

数据库的查询语言——SQL

与关系数据库一起走入人们视野的还有一种新的查询数据的方式, 说明型语言,最经典的莫过去SQL。要想了解SQL或者说明型语言的强大之处,首先我们先看看在这之前命令型是什么样的。绝大多数的编程语言都是命令型的,举个例子,假设你有很多种动物物种,你想找到所有的鲨鱼列表,会下下面这么搞

function getSharks() {

    var sharks = [];

    for (var i = 0; i < animals.length; i++) {

        if (animals[i].family === "Sharks") {

            sharks.push(animals[i]);

        }

    }

    return sharks;

}

但如果是SQL,你就会这么写了

SELECT * FROM animals WHERE family = 'Sharks';


可以看到两者差距,命令型语言告诉机器按照指定的顺序执行特定的命令,我们可以逐行调试,观察结果。但是说明型语言只表达了你要干嘛,比如要什么样的数据,数据的转换格式,比如求和求平均,但是具体怎么实现就不管了。这正是说明型语言的魅力所在,好学,好理解。(我们的产品SQL写的比研发6多了)。另外更重要的是它隐藏了实现的细节,这使得数据库开发人员能够在前端不需要任何修改的情况下引入新特性。就好像查鲨鱼的例子,命令型的结果实际指定了输出结果的顺序,但SQL却没有要求。这也给了优化更多的空间,例如一个当下很重要的特性——并发。因为没有了顺序的舒服,优化器可以自由的选择并发执行查找任务,使得处理更快。


Web端的查询语言 —— css

在浏览器中,说明型语言也很好用,假设你有一个海洋生物的网站,用户目前在浏览鲨鱼,所以导航栏中把鲨鱼会变成选中状态(selected)


这个时候,如果你希望标题选项背景改成蓝色,这样更明显,如果用CSS就很简单

li.selected > p {

    background-color: blue;

}

这个命令表示<li>标签中有seleted属性的所有<p>标签的背景变成蓝色

用XSL也很简单

<xsl: template match="li[@class='selected']/p">

    <fo:block background-color="blue">

        <xsl:apply-templates/>

    </fo:block>

</xsl:template>

但是如果你用javascript(一种命令型语言), 就疯了,这个代码不仅长,而且还不如CSS好懂。还有一个问题是,如果有了一个新的API更为高效,那你需要把整个代码改一遍。反过来说,css给了浏览器更多的优化空间,并且上层代码还什么都不用改。

var liElements = document.getElementsByTagName("li");

for (var i = 0; i < liElements.length; i++) {

    if (liElements[i].className === "selected") {

    var children = liElements[i].childNodes;

    for (var j = 0; j < children.length; j++) {

        var child = children[j];

        if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "P") {

            child.setAttribute("style", "background-color: blue");

        }

    }

}

在浏览器端和数据库端,用这种说明型语言CSS,SQL远比直接用API要方便且高效(因为有一大波人,做数据库优化的,做浏览器渲染的来帮你做优化)

不过多说一句,会用和用好的区别就在于你是不是理解底层的实现逻辑,同样是写SQL,明白底层实现的人写出来的SQL速度就是比一般人快。所以懒也是有代价的吧。

MapReduce 式的查询


mapreduce的介绍看这个

MapReduce其实是一种分布式处理海量数据的编程模型, MapReduce既不是一种说明型语言,也不是一种强指令性语言,它介于两者之间。实际的处理逻辑是由指令性代码实现的(指令性),但是而这些处理逻辑是由处理框架调用完成的.调用框架的实现对用户缺是隐藏的,而且这个框架本身的功能是很明确的(说明型)。 它依托于2个算子,map/reduce,这两个算子在函数式编程中非常常见。

举个例子,假设你是一个生物学家,当你没看到一种生物,你就把他写进数据库里,现在你要统计每个月你看到了多少次鲨鱼

如果是SQL,就这么搞

SELECT date_trunc('month', observation_timestamp) AS observation_month,

    sum(num_animals) AS total_animals

FROM observations

WHERE family = 'Sharks'

GROUP BY observation_month;

如果是支持mapreduce的mongodb的话,就这么写

db.observations.mapReduce(

    function map() {

        var year = this.observationTimestamp.getFullYear();

        var month = this.observationTimestamp.getMonth() + 1;

        emit(year + "-" + month, this.numAnimals);

    },

    function reduce(key, values) {

        return Array.sum(values);

    },

    {

        query: { family: "Sharks" },

        out: "monthlySharkReport"

    }

);

map, reduce函数的在一定程度上是受到限制的,他们必须是一个纯粹的函数,所谓纯粹,要求函数只能用给他们的数据作为输入,不允许有其他的副作用,比如查数据库,写文件等。这就允许框架在任何地方、任何顺序的调用他们,在失败的时候还可以重跑。

在查询中间加入javasrcipt代码在某些高级查询中,是一个很棒的能力,这不仅在mapreduce中可以,很多关系数据库也可以(Craig Kerstiens: “JavaScript in Your Postgres,” blog.heroku.com, June 5, 2013.)

但是对一些人来说,写两个map, reduce函数还是比写一个类似SQL这样的query要难的(语法问题,逻辑问题), 所以MongoDB支持下面这种说明型的查询,

b.observations.aggregate([

    { $match: { family: "Sharks" } },

    { $group: {

        _id: {

        year: { $year: "$observationTimestamp" },

        month: { $month: "$observationTimestamp" }

        },

        totalAnimals: { $sum: "$numAnimals" }

    } }

]);

这货其实就是json格式的SQL,所以没准故事会变成NoSQL经过不断的努力,重新发明了"SQL",尽管他们极力否认

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容