海量存储系列之序言—之一—之二—之三

​今天有人问我有没有数据存储的相关资料,我想了想。。虽然在这个领域内也算有点积累,以前讲课的ppt有200多页,但毕竟ppt的信息量有限。所以在这里将这个系列的部分内容在这里进行重新编排。这个内容会在我的轻博客和这里同步龟速更新。

主要将涉及到:
1. 数据库原理
   关系代数
   事务l
   sql
   k-v存储的使用
   数据存储模型
   数据写入模式性能和安全性分析
2. 倒排索引
3. 分布式kv系统
   数据的切分
   数据的管理和扩容
   数据安全性
   读写可用性
4. 硬件存储在淘宝的测试数据和分析
5. 淘宝在线数据存储检索经验介绍

海量存储系列之一

http://rdc.taobao.com/team/jm/archives/1344 海量存储系列之序言

那么 在走进海量存储与检索的世界之前,我们先看一看目前似乎觉得最落伍的数据库系统。丑话先说。。我也没在这个领域沉浸几年,所以其实里面很多的概念也是有可能模糊的,所以在这里写出来,一是希望我能把以前的积累再次重新的梳理一次,查缺补漏。二也是在这世界留下点记录。。表明我曾经来到过这世界,学到过这些东西。。

之所以从这里开始,一部分的原因是我本身是从这里开始接触存储这个领域的,另外一部分原因是因为,从我的私心来说,我认为数据库是生产力工具的开始,也将是生产力工具最终的归宿。
一个数据库,我们可以抽象的认为由下面的一个逻辑结构组成,刨除意义不大的视图,存储过程,外键限制等之后,我们就剩下了下面的这张图:
从API来说,也就是SQL,结构化查询语言,这个东东我们后面再去细说,先来看看这个关系代数模型。

之所以要从这里开始,主要的原因是因为,这是最受到关注的一个部分,自大从一开始做分布式数据层开始,被人问得最多的问题就是:1. 切分以后如何做join。2.如何进行分布式事务。。
可惜,现在我也没有一个方法能做到100%让您满意。。因为,没有银弹,只有取舍。
取舍的原则,也就是要根据,1) 你能做什么。2)你需要做什么。3)你能放弃什么。来决定上层系统的整体架构。传统的ACID的关系代数模型,在新的环境中,很难复用。但,原理没有变,做法上可以做微调
不知道各位在想起关系代数的时候,会想到什么?
http://www.bitscn.com/os/windows/200604/4364.html 我给大家一些解释(当然我没仔细看过:)
不爱看上面解释的童鞋们呢,也不用纠结,给大家一些简单的例子。
1. select * from tab where user = ?
这就是最简单的关系代数的例子,我们对他进行抽象来说,其实就是将大量数据中的一小批数据,按照某个要求查出来的过程。
2. select ename,dname from emp,deptwhere emp.deptno=dept.deptno and emp.deptno=30
这是另外一个例子,本质来说就是个∩的操作。
3. select count(*) from tab.
这是关系代数的第二个主要的用处,就是进行统计和计算,这类的函数有个专用的名字,叫做aggragate function.
恩,基本上有这个概念就可以了。
那么这里请联想,你所接触到的什么地方会有碰到有这样的一些计算呢?
数据库?对。不过不是废话么。。
还有就是hadoop的平台也会用到,hive pig.
那么,我们对这类关系代数进行一下简单的概念上的小结。
也就是用于处理数据的一类方法的抽象,最主要的作用是,按照某个条件选出一批数据,然后再进行一些简单的统计计算的功能。如是而已:)
那么,下面就有个疑问了,下层的数据库或存储,是如何进行这类的计算的呢?
那么请看下一个章节。
我们一起探寻一下,处理这类关系代数计算的方法有哪些

海量存储系列之二

http://rdc.taobao.com/team/jm/archives/1347 上一篇

在上一篇里面,我们对数据库的抽象的组成原理进行了简单的描述。在这一篇里面,我们一起来看看,如何能够使用kv这样的工具。来完成关系代数运算。
那么,让我们先来热热身:
这是一组数据,以pk作为主键,user_id和Name是外key.
那么,如果我要运行查询:Select * from tab where id = ?
应该如何进行呢?
这里需要一些额外的知识,在数据结构中,有那么一种结构,可以用于处理按照某个key找到value的过程,抽象来看,一种方法是二分查找法,一种方法是hash.
如果各位是java用户,那么二分查找的实现可以认为是个TreeMap的实现,而Hash的方法则可以认为是hashMap的实现。如果是个c/cpp的用户,那么就二分查找就对应map实现。而hash实现则对应stl里面的hash_map。
那么,这里的这个问题,我们就很容易可以解决了
以id作为map的key,以其他数据作为value,把所有数据都放入到map里面,然后再使用id=1作为key,从map中找到对应的value返回即可。(这一个部分,我们在后面的章节里面还会介绍,现在大家只需要有个大概的印象即可)
怎么样?是不是很简单?那么,我们来讨论更进一步的问题:
如果我想找到符合Select * from tab where user_id = 0的所有结果,应该如何去作?
仔细想想。那么第一种做法一定是这样。
把整个集合内的所有数据,都拿出来,然后找到user_id的数字,如果user_id=0,那么就认为是符合要求的记录,直接返回。
如果不是user_id=0,那么不匹配,丢弃这条记录即可。
这样一定可以找到所有符合要求的记录。
然而,这样作,带来的问题是,我有多少条记录,就需要进行多少次这样的匹配,那么,假设有100000000000000000条记录,就需要匹配这样多次,才能找到符合要求的记录。这是个悲剧。。
那么,怎么解决这个悲剧呢?
于是有些聪明人就又想起了map结构,hash或tree,不都可以按照k找到value么。那我们这里也可以利用这个map结构嘛。。
也就是说,以user_id作为key,id作为value,构建一个Map.不就又能进行快速查询了么。
于是,就有了数据库最重要的一个结构“索引” 这种以外键作为key,主键作为value的东西,有个专有的名字,叫做二级索引。
有了二级索引,我们的所有查询,都可以以接近O(LogN)(有序数据),或O(1)的效率找到我们需要的数据。是不是很爽?
但这不是银弹,你付出了空间成本,本质来说就是空间换时间的过程。同时,也会降低写入的效率。
怎么样?理解了没?如果自认为对这些都了解了,那么我们再来看一个问题:
如果我要找的是:Select …where user_id = ? And name = ‘袜子’
应该怎么做呢?
估计很多人都立刻又会想起那个Map,对的,但在这里,我想给出以下的几种查询的模式:
1. 遍历所有数据,取出一条以后,查看user_id = 0 and name=’袜子’是否符合要求,如果符合,则返回数据。
这是个合理的策略,空间最为节省,但带来的损耗是要遍历所有的数据。
2. 如果有个user_id -> pk的索引
,那么我们可以先按照user_id,找到一组符合要求的pk list.然后再根据pk list,再回到
取出符合要求的数据后,判断name=‘袜子’这个条件,如果符合,就返回,不符合,就丢弃。
这是个折衷策略,在空间和性能中,尽可能的找到个合理的区间的策略。
题外话,这个“根据pk list,再回到pk=>整个数据的kv表中,找出符合要求的数据后,判断name=‘袜子’这个条件,如果符合,就返回,不符合,就丢弃”的策略,在数据库有个专有名词,叫回表。
3. 组合索引
这是个新名词儿,但其实也是个很简单的概念。
直接上图:
:-) ,其实就是个很简单的策略,先比较user_id进行排序,如果user_id相同,那么比较name排序。
这样,假定我们有100000条记录,属于100个用户,那么平均来看,每个用户就只有1000条记录了。
原来要回表1000条记录才能找到符合要求的数据,而如果使用组合索引,这1000条,也可以使用O(log2N)或者O(1)的策略进行检索啦。
在很多场景中,都能够提升效率和速度。但付出的是更多的存储空间。
好啦,这篇就介绍到这里,留个题目给大家:
假设有这么一组数据,性别有4种,user_id是一对多的关系,如果我想查询
select * from tab where user_id in (?,?,?,?) and 性别=’不明’
如何进行索引构建能够获得比较好的效果呢?

海量存储系列之三

上一篇http://rdc.taobao.com/team/jm/archives/1349

首先是回答上次的问题。

假设有这么一组数据,性别有4种,user_id是一对多的关系,如果我想查询

select * from tabwhere user_id in (?,?,?,?) and 性别=’不明’

如何进行索引构建能够获得比较好的效果呢?

我个人认为,应该建立的是以user_id作为前导列,性别作为辅助列的索引,在大量单值查询时会有优势。

理由如下

1. 假定总数据量为N,user_id的区分度为N/10000 而性别的区分度为N/4

那么如果以user_id作为前导列,性别作为后列,那么查询的复杂度为O(logN+log(N/10000))。也就是说,第一次二分查找之后,下一次是在第一次的二分查找的基础上再去查找。而如果以性别作为前导,user_id作为后列,那么复杂度为

O(logN+log(N/4));

效率略差。

然后进入本次正题。上次介绍了关系模型,那么这次我们来介绍一下事务。

在一切之前,我想先给自己解嘲一下。。事务我自己也没有办法完全融汇贯通,因为每一个小的选择,都会导致效果的完全不同,所以有错请在后面一起探讨。

那么我们在这里,主要以单机事务作为入手点,然后阐述一下多机事务相关的知识点。我在这里只是想做一个引导,让大家能够对整个的知识体系有一个基本的认识,对于细节问题,会给出一些资料,而不会直接去进行讲解,因为篇幅所限.

一般来说,我们一提起事务,就会想到数据库,确实,事务是数据库的最重要的一个属性。但这似乎不是事务的本源,那么,让我们从更深层次去对事务进行一次思考吧:

事务,本质来说就是一组由一个人(或机器)发起的连续的逻辑操作,共同的完成一件事情,在完成整个事情之前,其所有的改动,都不应该对其他人可见和影响。而在事务结束之后,其一切的改动,都必须“全部”“立刻”对其他的人(或机器)可见。

然后,人们为了描述这一运作,使用了四个词汇,这也是很多面试的同学们折戟沉沙之处。J 不过这个以前我也不会,后来发现,理解了以后,确实有点用,所以这里也费一些笔墨吧。

原子性(Atomicity):也就是说,一组操作,要不就都成功,要不就都失败。不存在中间状态。

一致性(Consistency):一致性,也就是说,这个事务在提交或回滚的时候,对其他人(或机器)来说,数据的状态是同一的,不会出现中间的状态。最理想的状态下,就是说,数据提交后,所有的更改立刻同时生效,可惜,在计算机领域,这个做不到。。因为cpu运算,磁盘写入,内存写入,都是要时间的,内部一定是个顺序化的过程,所以不可能做到绝对的立刻同时生效。

所以一致性一般来说指代的是逻辑上的同时生效,比如,我要改A,B两行数据,那么,最简单的一致性保证就是,对A,B加锁,改A,B,然后对A,B解锁。

这样下一个人读到的一定是A,B的最新值啦。

(但这块有很多种解释,一般来说这是个最不明确的词汇)。

隔离性(Isolation):隔离,这是面试最容易挂的一个问题,其实我认为不怪我们,而是因为本身这个隔离性,是依托锁来进行设计的。

我们所知道的锁,主要有以下几种,1.读写锁,2. 排他锁

那么这四种级别其实就和这两种锁的实现有关,之所以要定义四个级别,其实原因也是因为,锁的范围越大,并行效率越低。而范围越小,那么并行的效率就越高。

读未提交: 其实就是什么锁也没有,所以数据的中间状态,是可能被其他人读到的。

读已提交:就是读写锁实现,读锁在查询之后会被释放掉,所以这样其他人可能会更改那些被释放了读锁的数据,这样当前事务再去读取的时候,就可能读取到被别人修改过的数据了,所以一个人在事务中读取到的某个数据,可能下次读取就变成别的数据啦。这就是不可重复读的意思。。

可重复读:也是个读写锁实现,读锁会阻塞其他人(或机器)的写,于是,只要是事务中读取到得数据,都被加了锁,其他人没办法改他们,于是就实现了可重复读咯。

最后是序列化,就是所有都顺序,一个大锁全部锁住J

持久性(Durability):持久性就是,事务执行后,就丢不了了,就算是整个中国被淹了,机器都没了,数据也不应该丢掉(不过基本做不到这个,也就是一个机器挂了不会丢数据而已。。)所有机房没了那数据也就没了。。

对于这块,给大家一些参考资料:

http://zh.wikipedia.org/wiki/%E4%BA%8B%E5%8B%99%E9%9A%94%E9%9B%A2

http://www.cnblogs.com/wangiqngpei557/archive/2011/11/19/2255132.html

这些讲的不错,浅显易懂是我的最爱.

好啦,为了保证我写的东西不会被”qing”这个大怪兽再次吃掉。。我先发这些。

在下一个章节,我们继续在事务这个领域徜徉,给大家介绍一下,在单机上面,事务是如何进行的。

http://rdc.taobao.com/team/jm/archives/1365 下一篇 单机事务





posted on 2013-04-24 22:29  Java码界探秘  阅读(120)  评论(0编辑  收藏  举报

导航