东哥IT笔记 Blog

0

关系型数据库进阶之查询优化

在前面几篇文章中,我们已经介绍了总体构架,客户端管理和查询管理。在查询管理中,有一个很重要的部分我们没有介绍,那就是查询优化,这也是本文所要介绍的内容。 所有的现代数据库都是基于cost进行优化的(Cost Based Optimization, CBO)。总体的思想就是看每一个操作的cost是多少,然后找出一个cost最小的路径来执行这些操作并获取结果。 本文会首先以最常的三种join为例来进行介绍,看看哪怕是简单的join,它背后的优化是如何进行工作的。在这些例子中,我们关注时间复杂度,这个其实和真实的数据库优化器的计算是有所不同的,它真实会关注CPU消耗,磁盘I/O消耗以及内存的需求。时间复杂度和CPU消耗其实是差不多的,我们这种懒人就直接使用时间复杂度来进行分析了。当然有时候我们还需要考虑磁盘的I/O消耗,因为很多时候真正的瓶颈就是在于磁盘I/O而不是CPU的使用率。 JOIN的操作 我们会来看三个最常见的join操作,merger Join, Hash Join以及Nested Loop Join。在正式开始之前,我们来引入两个名词,outer relation和inner relation,很简单outer relation就是join左边的数据集,inner relation就是join右边的数据集。比如A JOIN B, 我们把A称之为outer relation而B称之为inner relation。并且假设outer relation有N个元素,inner relation有M个元素。我们可以很容易知道A JOIN B和B JOIN A其实是不太相同的。 Nested loop join Nested loop join是最简单的一种情况...

0

关系型数据库进阶之查询管理

在前面几篇文章中,我们已经介绍了总体架构以及客户端管理,今天我们来继续介绍查询管理。 毫无疑问,查询管理是数据库的核心中的核心,也是最重要最难的地方。这个部分,哪怕是写的不好的query也会被转换成尽量快的方式来进行执行,在执行之后会把结果在返回给客户端管理。主要有一下几个步骤: 解析query,看是否有效 重写相应的query,去除一些没有必要的操作,主要是做预优化 优化相应的查询,主要是提高性能,转化成高效的执行计划和数据访问计划 编译计划 最后,执行计划 我们主要来讨论一下前三个部分,因为他们基本就是query manager最核心的地方了。 query解析 所有的SQL语句都会发送到解析器来进行语法的检查。query语句中的任何语法错误都会导致query的结束,比如你把SELECT写成了SLECT,那么语句就会在这里的检查结束并报错。当然也不是仅仅这些简单的检查,还包括一些复杂的,比如你把WHERE写到了SELECT之前,那么这里也会报错。 紧接着会检查相关metadata,包括: 相应的表是否存在 相应的域是否在表中存在 对应的操作在对应的列数据类型上是否有效(比如你不可以把一个整数和字符串相比较,或者你不能在整数上执行substring()操作等) 做完这些检查后,它会检查你是否有相应的表格的读写权限。 假如这些检查都ok,那么相应的SQL query会被转换成内部的表示形式(通常是一个tree),然后会被送到query rewriter中。 Query重写 在Query解析之后,会把相应的内部表示形式传到Query重写这里,那么这里需要的操作就是: 预优化query 避免不必要的操作 帮助优化器找到最佳的方案 Query的重写其实就是把一系列预定义的规则在这个query上运行,假如这个query符合相应的规则,那么就根据这个规则进行重写。下面是一些可选规则: View的合并:假如你在query中使用view,那么就会把view的代码合并到相应的SQL语句中 子查询的展平:有子查询之后,是非常难进行优化的,所以这里会尽量把子查询展平了用来取代相应的子查询。举例如下: 这个语句会被转换成下面这个语句: 去除不必要的操作:比如你使用了DISTINCE,但是你又有一个UNIQUE的限制来保证数据不会重复,那么DISTINCT的关键词就会被去除。 消除冗余连接:比如你有两个一样的join条件,只是有一个条件被隐藏在一个view中或者别的不容易注意的地方,那么这个同样的join条件其实就是冗余的,这里会去除掉。 常数的算术评估:假如你写了一些需要计算的公式,那么它就会在这一步中计算。比如WHERE AGE > 10...

0

关系型数据库进阶之客户端管理

在上一篇文章中我们介绍了数据库的总体架构,今天我们将和大家来一起分析一下其核心组件中的重要组成部分:客户端管理。 客户端管理,顾名思义它是用来处理和客户端之间的信息交互的。客户端可以是一个web的server或者一个终端的用户/应用。客户端可以通过各种不同的方式来访问数据库,比如JDBC, ODBC, OLE-DB等等。 当连接数据库的时候,会执行下面这些步骤: 客户端管理首先检查你的权限和认证,看你是否有相应的访问数据库的权限。这些权限都是通过DBA来设置的。 然后,它会检查看是否有可用的进程(或者线程)来管理你的query 再检查看相应的数据库是否处于很重的负载情况下。 得到相应的资源,这里会有一个timeout,假如一定时间还没有得到资源,则会timeout,并且关闭相应的连接以及返回一个可读的错误信息。 发送query到相应的query manager,你的query就被执行了 Query 的过程并不是说全部返回,可能是一部分一部分返回的,所以它会把部分的返回值保存在一个buffer中,并且开始把它们分步发送回客户端。 假如有什么问题,它会断开连接,然后给出一个清晰的错误解释,并释放相关的资源 这就是整个数据库的客户端管理流程,其实还是蛮简单的,下一篇我们将会具体介绍更为核心的Query Manager部分。 参考文章:http://coding-geek.com/how-databases-work/

0

关系型数据库进阶之总体架构

一提到关系型数据库,我们可以看到它被在各个地方使用。有很多不同的关系型数据库,从最小的SQLite到复杂的Teradata。有很多文章在介绍如何使用数据库,但是很少有问题去深入地介绍它是如何工作的。假如你搜索“关系型数据库是如何工作的”这样的关键词,你会发现很少有问题进行深入详细的介绍。有时候,我们不禁会问,关系型数据库是不是太旧了,以至于都没有什么人来深入分析其中的内容? 作为一个开发者,我们需要有一点好奇心去深入看看我们每天使用的数据库内部是怎么样,假如你一直没有时间或者机会去深入了解其中的原理,那么本文将会是你的一个很好的选择。本文之所以称之为“进阶”是因为我们不会介绍如何去使用一些Query,而是假设你已经有了这些基础的知识,我们更注重其中的机理讲解。 一个数据库其实是信息的集合,而这个集合是可以很方便地访问和修改的。假如往简单的方面来想,那么它就是一系列的文件。事实上,最简单的数据库,比如SQLite其实就是一堆文件。当然SQLite又不是简简单单的一堆文件,因为它允许你: 使用transaction来保证数据的安全和连贯 能够很快的处理数据,哪怕是很大的百万量级的数据 总得来说,一个数据库可以由下图几个部分组成: 从这张图可以看出,总得核心有一下几个部分: 核心的组件: 进程管理: 很多数据库都有一个进程/线程池需要管理。更有甚者,为了做到纳秒级的效率,很多数据库都使用他们自己的线程而不是系统的线程。 网络管理:网络的I/O其实对数据库来说很重要,尤其是在分布式数据库中更为明显。这也是为什么很多数据库都有他们自己的管理者。 文件系统管理:数据库的第一个瓶颈就在于磁盘的I/O。需要一个文件系统管理来处理相关的内容,甚至取代系统的文件系统。 内存管理:为了避免磁盘I/O的损失,就需要大量的RAM。随之而来的就是,当你处理大量的内存的时候,你就需要一个高效的内存管理。尤其是你同时有很多query在使用内存的时候。 安全管理:主要管理用户的认证。 客户端管理:管理客户端的连接 工具: 备份管理:保存和还原数据库。 恢复管理:在数据库奔溃后能够以一致的状态启动数据库。 监测管理:记录数据的运行log,并且提供相应的检测工具。 Admin管理:用于存储元数据比如表的名字,结构,以及管理数据库,schema,表格空间等等。 Query管理: Query的解析:检查一个query是否有效 Query的重写:预先优化一个query Query的优化:优化一个query Query的执行:编译和执行query 数据的管理: Transaction管理:处理transcation cache的管理:在使用数据在前把数据放到内存,以及在写到磁盘之前把数据放到内存中 数据访问的管理:如何从磁盘访问数据 本文就从宏观介绍了数据库的组成部分,我们将在后面的文章中来分别主要介绍以下的内容: 客户端管理 查询管理 数据管理 参考文章:http://coding-geek.com/how-databases-work/

0

SVG介绍

SVG是1990年代构思出来的,很长一段时间都不太受人待见,但现在则发展得越来越好,越来越多的人推荐和喜欢使用它。其实在2000年左右,对于SVG的支持还是很少的,直到2017年很多web浏览器都开始支持SVG,很多的向量程序已经支持导出SVG。当然毫无疑问,SVG已经越来越广泛地使用在WEB开发中。 我们来看看根源,其实SVG的流行并不是偶然。虽然一些传统的图片格式,比如JPG和PNG对photograph来说很完美,但是其实SVG对现在web开发中的扩展性,响应性,交互性,可编程性,性能以及可访问性这些要求来说,是一个近乎完美的选择。 什么是SVG?为什么要使用它? SVG是一个基于XML的矢量图形格式。XML使用的是HTML的tag,当然它更加严格而已。比如说,你不能省略结束标签,因为这会使得SVG的render出问题。 给你一个初步的感觉,下面就是一个SVG的代码,它就是画一个白色的圆,有一个黑色的边框 <circle cx=”100″ cy=”100″ r=”50″ stroke-width=”4″ stroke=”#000″ fill=”#fff” /> 所以,其实说白了SVG就是一个很平的文本,有描述线的,曲线,形状,颜色和文字的。我们可以看到其实它的可读性很好,而且当我们把它放到HTML中的时候,可以很方便地通过CSS和Javascript来修改它。这样的特性是传统的文件格式比如PNG, GIF, JPG等所不具备的。 SVG是一个W3C的标准,因此他可以很方便地和别的语言和技术相结合,比如JavaScript,DOM,CSS以及HTML。从另一个角度来说,作为业界权威的W3C标准定,SVG可能会成为所有浏览器关于矢量图标准的定义。 SVG的过人之处就是它可以解决很多现代web开发的问题,我们来具体看看这些问题 可扩展性和响应性 SVG是使用形状,数字和坐标而不是像素来渲染图片的,这就使得他们可以和分辨率没有关系并且无限地扩展。其实你一想想,就会发现其实他们的创建图片的方式和你拿一支笔在那边画画是一致的。只有比例在改变。 有了SVG之后,你可以使用形状,路径和文本元素来画各种各样的图片,而且你可以确认即使在大的scale下也是很清晰的。 相比之下,GIF,JPG以及PNG都是有固定的尺寸,这也使得他们在扩大的时候显示就会有问题。当然,很多基于像素的图形都已经证明有其固有的优势,但是至少在无限扩展性这个方面和SVG相比还是有很大差距的。 可编程性和交互性 SVG是完全可以编辑和编写脚本的。各种各样的动画和交互可以通过CSS或者Javascript加入到inline SVG图形中。 辅助功能 SVG文件是基于文本的,所以当嵌入到web页面中的时候,可以搜索到和加index。这使得他们对屏幕阅读者,搜索引擎以及其它设备都很友好。 性能 一个影响性能的重要方面就是文件的大小,尤其在web页面中。而在这个方面,SVG和bitmap文件格式相比通常来说大小要小很多。 常见用例和浏览器支持 SVG有很多的常见用例,下面我们来看一些比较典型的例子 普通插图和图表 任何传统的使用铅笔和钢笔画的图形都应当可以很方便地转换成SVG的格式。 Logos和icons...

如何使用MySQL保存一个图片并且用PHP得到它 0

如何使用MySQL保存一个图片并且用PHP得到它

首先,我要说的是把一个图片保存在MySQL中,再读出来是一个很不推荐的做法。我们只是看看假如真的要这样做,该如何做,最后我会和大家聊聊一般我们如何处理这种使用情况,以及不推荐这样做的原因。 首先图片是一个BLOB(Binary Large Object)是可以用来存储二进制的数据。这就是用来保存图片,文件等等的数据。因为这种object通常都很大,所以我们需要定义一个很大的域来保存信息。如何通过PHP来插入呢,其实很简单: 1)读取图片成二进制 2)准备插入到数据库 3)使用SQL语句插入相关数据到数据库 下面我们来看看简单的PHP代码: 这里我们使用了fread()来把图片读取成比特,有了相关比特数据之后,就是简单地把它插入到BLOB的列就可以了。我们例子中image列是一个BLOB的数据类型。需要注意的是你选择的BLOB的列大小需要能够保存相应图片的大小,因为MySQL还是有不同大小的BLOB数据类型的。 下面我们来看一下如何获取图片数据,同样需要三个步骤: 1)设置content的type到图片的类型 2)找到图片,并且把它从数据库中拿出来 3)显示图片 相关的代码如下: 我们把数据从image列中拿出来,并且保存在一个变量中,然后我们设置相关content-type为image/jpeg就可以了,然后就可以显示相关的内容了。 需要注意的是不要再header()调用之前打印任何部分,否则在调用header的时候会报错”Headers already sent”。 其实说白了,这个和别的BLOB得数据的插入读取是一样的,并没有任何差别。 下面我们来谈谈为什么不要把图片保存到数据库中呢?因为数据库一般为了查询的方便会进行分页,而一些大的数据则会导致查询变得很慢,所以我们还是不要这样做为好。 那么推荐的做法是什么呢?我们一般会存储相关文件的路径就可以了。当想要找相关文件的时候,只要找到相关的路径就可以了,然后再根据相关的路径去磁盘找到需要显示的文件就可以了。这样做有两个好处:1)这样数据库的数据就很小了。 2)有了文件的名字,我们就可以用文件名来查找相关的文件了。 好了,这就是本文要讲的内容了。 参考文章:https://www.dreamincode.net/forums/blog/114/entry-3598-inserting-images-into-mysql-and-retrieving-them-using-php/

0

如何在SQL中得到两个表的不同行

有时候,我们需要比较两个表的差异,希望能够返回两个表不同的行,那怎么才能有效快速地得到这个结果,本文就来做一个简单的介绍: 表格准备 我们来假设有下面两个表(PostgreSQL 语法): 使用UNION 我们的第一反应大概就是看看能不能使用UNION,我们先得到表1-表2的内容,然后再得到表2-表1的内容,再把两者union起来,如下面的语法所示: 这样之后,就会得到下面这样的结果: a |b |c |–|–|–| 1| 2| 3|10|11|12| 但是这样有一个问题,就是每一个表格我们都访问两次。有没有更好的方法呢? 使用NATURAL FULL JOIN 我们可以使用下面的语句来实现: 这时候的返回值是这样的: 为什么呢,因为NATURAL FULL JOIN其实是使用相同的列值就行join的,这里我们两个表各产生了一个新的列t1和t2,在相同值的行,t1和t2列的值就会都存在,而在不同值的行,则只会有一列存在,可能是t1列或者有可能是t2列。我们只要把这种情况过滤出来就可以了。 这里需要注意的是,当值是NULL的时候,判断的时候会遵循下面这个表格: 这里我们可以看到R IS NULL和NOT R IS NOT NULL其实是不同的,所以我们上面提到的natural full join其实等同于下面这样: 另外一种写法是我们使用JOIN …...

0

Database数据一致性介绍

数据一致性的场景很简单,假如我们把一个值赋给一个变量,那么我们之后立即再去读这个变量,突然发现它的值和我们想象的不一致,是不是很沮丧,对的,这就是数据的一致性。 而在使用分布式的数据库的时候,尤其是那些对数据一致性不是很严格保证的情况下,这种情况经常会发生。也许你会问,什么?难道他们不应该给我们保证数据是一致的吗?我的回答呢,总得来说最后他都会能够保证的,但是这个中间的间隔时间则是取决于不同数据库的实现。 它们这样做是因为有一个trade off在里面,有些数据库为了提供可靠性和性能,则会牺牲掉一致性的保证。而有些数据库则允许你进行选择,你是要强一致性还是要高性能,比如Azure的cosmos DB和Cassandra。 数据库请求分析 我们来分析一下数据请求的时间线是什么样的,理想状况下,你的请求会被立即执行。 然而事实上肯定不是这样,我们需要时间连接存储数据的地方,然后还需要时间返回response给你。如下图所示: 这里最好的保证就是请求是在你调用和完成中间的某个时间点完成的。你可能会想,这个和你给变量赋值是类似,比如x=1,然后再读,你会发现它是1(前提就是这中间没有别的thread再写这个x)。但是假如是一个分布式系统,就不是这么简单了,你需要考虑别的server是如何更新到这个新的值的,这里就有一个设计的trade off,尤其是可靠性,性能和一致性之间的考虑。 假如我们有一个简单的分布式的键值存储,他由一系列的副本组成。这些副本在他们中间选择了一个leader,然后只要这个leader才能做写的操作。当这个leader接收到了一个写的请求,它会广播这个请求到所有的副本。即使这些所有的副本受到的请求是一样的顺序,他们更新的时间都是不同的。 所以,现在假如你来考虑读的情况,你该怎么处理?因为读是可以发生在leader上,也可能发生在任何一个副本上面,但假如所有的读都要到leader这边,那么显然性能就不会好。从另一个方面来说,假如我们让读发生在副本上,这样一来性能就会好很多,不管我们系统扩大到什么程度,性能显然都不会有大的问题。但是这里会有一个额外的问题,那就是因为每一个副本的更新可能都会不及时,所以他们的值就会有差别,这就会导致我们在不同副本读出来的值会有差异。 所以,最终这里有一个trade off,那就是系统的性能,可靠性和一致性。为了更好地理解这个关系,我们来先看看我们是怎么定义一致性的。 强一致性 假如所有的读写请求都是通过leader进行的,那么不管有多少的副本,对客户端来说,他们的操作都是原子的,他们总能够得到他们想要的数据。 当然因为一个请求并不是理解执行的,总归需要一个时间,数据的更新是发生在执行和完成之间,假如我们做这个保证,那这就是强一致性。 但是总是通过leader来执行也是有问题的,比如说假如客户端发送了一个请求给leader,在客户发送请求到leader之后,leader还是认为它自己是leader,但是事实上它已经不是了。这个时候,leader还来处理这个写的请求,那么系统其实就已经不是强一致性了。有处理这种情况,那么leader就需要问一下大多数副本,是不是还认为它是leader,只有这种情况下才能够执行写的请求,并且把response返回给客户端,这样的操作,显然增加了读的时间,性能必然会下降。 顺序一致性 前面我们讨论了强一致性,所有的读都会去leader那边,而事实上,这样做就会导致读的速度很慢,因为leader还需要和大多数副本进行商议。这种情况下为了提高读的性能,我们需要允许副本支持读。 尽管副本的读是在leader之后的,但是它的更新顺序始终是和leader一致的。假如客户端A总是读副本1,而客户端B总是读副本2,那么这两个客户端看到的数据就是不相同的,因为他们的副本并不是总是一致的。如下所示: 这里两个副本虽然更新的时间不同,但是他们的更新顺序则是相同的,这就是所谓的顺序一致性。 最终一致性 尽管我们希望增加读的速率,但是这里还有一个问题,副本假如出问题了怎么办?我们需要允许客户端查询所有的副本,从而来增加一致性。但是这里的对一致性来说就有了新的挑战,比如说假如客户端1开始是在副本1上查询,而副本2是落后副本1的,在某一个时间点,假如客户端切换到了副本2,会发现读出来的值竟然是以前的值,这个显然就是有问题的。这里唯一保证的就是所有的副本最终读出来的值都是一致的,这就是所谓的最终一致性。 使用最终一致性的系统是非常复杂的,因为这个和你平时的读写有很大的差别。可能会遇到很多很难发现的问题。所以这里就需要根据你的应用来看,它最终需要的是什么。比如说,这个最终一致性的系统对你想看看有多少人访问你的系统这样的应用是足够的,但对那些付费系统则显示强一致性是更好的选择。 总结 有很多文章会介绍各种一致性的模型,但是背后的理论其实是一致的:越严格的一致性,则需要牺牲越多的性能,以及发生问题时,可靠性越低。这就是所谓的PACELC理论。这个理论认为,分布式系统中有网络分区P,那么我们就需要在可靠性(A)以及一致性(C)之间进行选择。即使在没有分区的情况下正常运行,我们也必须在延迟(L)和一致性(C)之间进行选择。 参考文章:https://robertovitillo.com/what-every-developer-should-know-about-database-consistency/

Java中的switch语句 0

Java中的switch语句

很多人在学习Java语言的时候,应该就听说switch语句和if else差不多,其实事实上真的是这样吗?尤其是当你真的写代码的时候,是使用一个if else还是使用switch呢?让我们带着这些问题来仔细阅读以下本文的内容吧。 首先我们来看一个简单的代码,他们实现的功能类似,但分别使用的打包开关(packed switch),稀疏开关(sparse switch)以及if else。 这段代码非常简单,也许你的老师会告诉你,这些代码最终编译出来都是一样的,那么事实上真的如此吗?其实假如你看jvm的代码的话,会发现它们其实是不相同的。我们可以使用下面命令来看jvm的代码: 上面类的的jvm代码如下所示: 下面我们就来仔细看一下,首先来看if else, 和我们想象得差不多,一个一个地进行比较,要是有相等的就return。这个应该和我们想象得比价类似吧。 下面来看switch的情况,switch有两种情况,一种就是各个switch的值比较靠近,如下: 它编译的结果如下: 这里你会发现其实有一个tableswitch的东西,那这个是什么呢?在JVM spec中有讲到。 简单来讲,就是说其实类似做了一个key value的表,在key范围内的值,则会通过index直接找到相应的值,而范围之外,就直接返回默认值了。 而假如你使用是一个稀疏switch,也就是说各个case之间的值变化很大,他会怎么来实现呢? 他编译的结果如下: 这里我们看到他不再使用tableswitch了,它使用的是一个lookupswitch,其实说白了,他就是把所有的key排序,然后查找的时候就可以在一个有序表中进行查找了,这个效率其实相对tableSwtich就有一些差别了。当然和if else比则又不相同了。 好了,看了本文之后是不是才恍然大悟,原来我们平常看到的简单代码背后竟然有这么多不同的逻辑。 参考文章:https://www.dreamincode.net/forums/blog/1748/entry-4558-switch-statements-in-java/

0

创作你的第一个CSS的艺术品

很多人都知道如何用CSS来实现网页的各种效果,但是提到用来CSS来创造艺术,都不是特别清楚它是做什么的。本文会首先介绍一些CSS的基础,然后会和你一起看看如何来实现一个CSS艺术品,下面让我们开始吧: 一些重要的CSS特性 渐变(gradient) 我们可以在背景或者背景图片中定义渐变,你想使用多少颜色都可以,不同的值之间用逗号分开即可。梯度有两种: 线性渐变:可以有方向(上下左右)或者角度来控制。 径向渐变:可以通过给每种颜色设定一个百分比,并设置形状来控制。 下面是一个很好的产生渐变的网站:cssgradient 阴影效果 (box-shadow) 同样你可以设置一个或多个阴影效果,中间用逗号分隔开即可。 我发现inset这个属性特别有用,他可以把阴影效果从外阴影改成内阴影,看起来就真实多了。 关于阴影效果的网站可以使用这个:cssmatic 剪切路径(clip-path) 剪切路径在CSS艺术品中非常有用,没有它你可能需要花费很多时间才能达到同样的效果。通过指定要显示的区域,他可以帮助你画圆形,多边形和路径。 一个非常好的剪切路径的网站:bennettfeely 创作你的第一个CSS艺术品 了解了上面一些基础信息后,下面来开始创作我们的第一个艺术品吧。我们的目标就是做出下面这个图形: 下面是HTML的内容: 第一件事就是为coffee container实现相关的风格(coffee-cont),我们把他设为 position:relative,然后他里面的div都设为absolute (除了coffee-cup div),这样里面的位置就会取决于他的父节点的位置。 下面就是coffee-cup相关的设置,我们使用了radial-gradient来设置背景,这样可以让中间比两边看起来稍微亮一点: 这时的效果如下所示: 然后,我们使用radial-gradient来设置这个coffee-cup的before元素。注意这里我们使用了z-index:9来让它显示在杯子上面: 下面我们再来把杯子口设置一下: 下面我们来画杯子的手柄了,这里我们使用z-index:-1,他就会显示到杯子的后面去了: 下面我们来画下面这个杯托,我们适应一个圆的radial-gradient,让中间明亮一点。我们同时还需要一个阴影,让它看起来更自然,我们使用黑色的box-shadow来实现: 最后,我们再在::after给杯托也加一个阴影。 大功告成,我们想要的杯子就这样实现了。恭喜你,去画一个你喜欢的图形吧。 参考文章:https://dev.to/laasrinadiaa/make-your-first-css-art-29jo