转 海量存储检索原理系列文章

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               


海量存储检索原理系列文章

作者:WhisperXD

来源:http://qing.blog.sina.com.cn/whisperxd




Nov 20

海量存储之序言

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


主要将涉及到:
1. 数据库原理   http://qing.weibo.com/1765738567/693f0847330005sm.html
   关系代数   http://qing.weibo.com/1765738567/693f0847330005v7.html
   事务  http://qing.weibo.com/1765738567/693f084733000672.html
   数据存储模型
   数据写入模式性能和安全性分析
   
2. 倒排索引
3. 分布式kv系统
   数据的切分
   数据的管理和扩容
   数据安全性
   读写可用性
4. 硬件存储在淘宝的测试数据和分析
5. 淘宝在线数据存储检索经验介绍

http://qing.weibo.com/1765738567/693f0847330005v7.html 下一篇


Nov 20

海量存储系列之一

http://qing.weibo.com/1765738567/693f0847330005sk.html 海量存储系列之序言

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

之所以从这里开始,一部分的原因是我本身是从这里开始接触存储这个领域的,另外一部分原因是因为,从我的私心来说,我认为数据库是生产力工具的开始,也将是生产力工具最终的归宿。

一个数据库,我们可以抽象的认为由下面的一个逻辑结构组成,刨除意义不大的视图,存储过程,外键限制等之后,我们就剩下了下面的这张图:

海量存储系列之一


从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.
这是关系代数的第二个主要的用处,就是进行统计和计算,这类的函数有个专用的名字,叫做aggregate function.(感谢@KR明

恩,基本上有这个概念就可以了。
那么这里请联想,你所接触到的什么地方会有碰到有这样的一些计算呢?
数据库?对。不过不是废话么。。
还有就是hadoop的平台也会用到,hive pig.

那么,我们对这类关系代数进行一下简单的概念上的小结。
也就是用于处理数据的一类方法的抽象,最主要的作用是,按照某个条件选出一批数据,然后再进行一些简单的统计计算的功能。如是而已:)

那么,下面就有个疑问了,下层的数据库或存储,是如何进行这类的计算的呢?
那么请看下一个章节。
我们一起探寻一下,处理这类关系代数计算的方法有哪些




Nov 22

海量存储系列之二

​http://qing.weibo.com/1765738567/693f0847330005sm.html 上一篇

在上一篇里面,我们对数据库的抽象的组成原理进行了简单的描述。在这一篇里面,我们一起来看看,如何能够使用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://qing.weibo.com/1765738567/693f0847330005v7.html

首先是回答上次的问题。

海量存储系列之三

假设有这么一组数据,性别有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://qing.weibo.com/1765738567/693f08473300067j.html 下一篇 单机事务



Nov 26

海量存储系列之四


单机事务:

其实在上面介绍ACID的时候

我们已经提到了一种最简单的实现方式,就是锁的实现方式。

从原理来看,事务是个变态而复杂的事情。其实如果是序列化的话呢,那么实现起来一定是非常简单的。

但问题就在于,这样性能实在比较低,于是,就有了非常多的方案,为了能哪怕减少一个地方的锁,或者降低一个地方的锁的级别,就付出大量的时间和代码加以实现。

那么,让我们以崇敬的心情,去拜读一下他们的劳动成果吧~

 --------------------------------------------------------------------------------

 

在上一篇中,我们谈了事务管理的四个核心要素,其中有两个要素是和性能紧密相关的,其实也就是需要涉及到锁的,一个是隔离性,一个是一致性。

一致性问题和隔离性问题,我们都可以归结为一个问题,他们都用于定义,什么时候数据可被共享,什么时候数据必须被独占。而这些决策,就最终决定了整个数据库系统的并行度,也就直接的决定了多线程并发时的性能指标。

 

如果要改一大批数据,又必须保证这些数据要么都出现,要么都不出现,这时候就有个难题了:因为这些数据不可能在同一个时间被选出,更不可能在同一个时间被更改。

于是就必须想个办法来假装达到这个状态,于是我们就需要一种方法,使得针对不同数据的更改,不同人(或机器)不打架。而如果出现对相同数据的更改,则要将更新进行排队。

这个排队可供选择的方法,就我知道的有:1,排他锁。2. 读写锁。3. Copy on write(MVCC) .4. 队列。5. 内存事务。这些方式。

 

从性能来说,排他锁最慢,而读写因为读可以并发,所以效率稍高,但写和读不能同时进行。3. Copy on write(MVCC) 则读取和写入之间可以互相不影响,所以效率更高。队列这种方式,内存时效果很好,省去中断上下文切换的时间。内存事务,目前还在研究阶段,具备很大潜力的东西。

 

排他锁,队列和内存事务,在目前的数据库中用的相对较少,我们就不在这里说了。

这里主要说两种实现,一种是读写锁,一种是MVCC.

 

先说读写锁,也是隔离性中“读已提交,可重复读”两种实现中最重要的底层实现方式。

简单来说,就是如果一个人在事务中,那么他所有写过的数据,所有读过的数据,都给他来个锁,让其他小样儿都只能等在外面,直到数据库能确定所有更改已经全部完成了,没有剩下什么半拉子状态的时候,就解开所有的锁,让其他人可以读取和写入。Hoho,就是这个了。

 

那么MVCC呢,其实是对读写锁的一个改进,有一批大牛们,说你们这读写锁,写的时候不能读,读的时候不能写,并行度太低了,我要做个更牛B的,写不阻塞读,读不阻塞写的东西来超越你们。

于是他们想起了copy-on-write.鼓捣了个MVCC数据库出来。。。

题外话,现在的甲骨文,之所以能在数据库领域保持优势地位,有个很重要的原因也是因为他们是很早就在商业数据库系统中实现了MVCC的数据写入引擎。

所以他们的Thomas Kyte 技术副总裁也就有了在他们的最牛逼的oracle专家编程里面有了吹嘘的资本 XD .

 

这里我们要着重的介绍一下MVCC,因为这东西看起来非常的精妙而美丽。。。现在大量的分布式类存储中,也都在借鉴这套模式中的很多部分来增加自己的并行度,以提升性能。比如megaStore.比如percolator。

我们在读写锁的实现中,提到了写读的相互阻塞问题,MVCC则使用copy-on-write来解决这个问题。

如果一个人在事务中,会先申请一个事务ID,这个ID是自增的,每个事务都有他自己的唯一的ID,那么他写过的数据,都会被转变为一次带有当前事务ID的新数据,在读取的时候,则只会读取小于等于自己事务ID的数据。这样实现的东东,语义上来说,与可重复读就一样了。而如果读小于等于全局ID的数据,那么这样的实现,就是读已提交了。

 

一般来说,MVCC只实现了四个级别中的第二级和第三级,其他的就没有啦,不过这两个是我们最常见的级别。所以也就大家同乐,同乐了~

 

有了这个东西,我们的一致性也就很容易保证了,因为一个事物和他对应的版本号对应,又有更改后的数据和更改前的数据,如果要提交,那么就只需要很简单的让更改后的数据生效可见即可,这样我们可以将大量的更新中要做的事情,都在事务过程中进行,这样,比原有的基于读写锁的必须在commit时候一起做掉来说,commit这个操作就轻量化了很多,于是,就可以支持更多的人(或机器)持有事务状态了。

 

很美妙吧?

我一致认为这是oracle当年的核心竞争力,不过现在基本上是个数据库就用了这一套,我们就不在多嘴啦~

 

解决了一致性和隔离性,剩下的是原子性和持久性,原子性么,一般来说就是要么都成功,也就是新版本数据都让他生效,要么就都失败,也就是让和自己事务ID对应的所有修改都无效即可。也很好就解决掉了。持久性。这个就是后面我们要在写入模型里面介绍的东西了,基本上来说就是写磁盘策略的事情。

 

到这里,我们单机ACID的实现大概思路,就给大家介绍过了。下一个章节,我们还要用很多的文字,来向大家介绍在分布式场景中我们面临的事务的难题,以及“我所知道的”百花齐放的解决方法。

http://qing.weibo.com/1765738567/693f0847330006ao.html?rnd=0.6134993201121688




Nov 27

海量存储系列之五

http://qing.weibo.com/1765738567/693f08473300067j.html 上一篇


在上一章节,我们一起浏览了如何进行单机事务操作。下面我们来看一下分布式场景中我们碰到的问题吧。

 

需要说明的一点是,这里涉及到的权衡点非常的多。就我短短的工作经验里面,也只是能够简单的涉猎一部分,因为在事务这个领域,目前大家都在尝试提出各种各样的不同的方法,而在taobao,我们目前也没有完美的解决这个问题,更多的是在权衡,在金钱和开发成本之间,做出选择。

 

那么,我们就先从问题开始,来看一下原来的事务出了什么问题。

在事务中,有ACID四种属性。(见上篇文章)

 

在分布式场景中,我们看引入了什么因素,导致了什么样的新问题:

1.      延迟因素:光是我们所知最快的信息载体了,各位可能都会从潜意识里面认为光传输信息不就是一眨眼的事情而已。那我们做个简单的计算吧(感谢@淘宝叔度,第一次在分享中让我对这个问题有了个数值化的印象。):

北京到杭州,往返距离2600km ,光在真空中的传输速度是30wkm/s。在玻璃中的速度是真空的2/3。算下来,最小的请求和响应,之间的延迟就有13ms。并且,因为光在管子里走的不是直线,又有信号干扰等问题,一般来说要乘以2~3倍的因子值。

所以一次最小的请求和响应,时间就差不多有30ms左右了。

再想想TCP的时间窗口的移动策略,相信大家都能意识到,实际上延迟是不可忽略的,尤其在传输较多数据的时候,延迟是个重要的因素,不能不加以考虑。

并且,延迟 不是 带宽,带宽可以随便增加,千兆网卡换成万兆,但延迟却很难降低。而我们最需要的,是带宽,更是延迟的降低。因为他直接决定了我们的可用性。

2.      灾备因素:单机的情况下,人们一般不会去追求说一个机器物理上被水冲走了的时候,我的数据要保证不丢(因为没办法的嘛。。)。但在分布式场景下,这种追求就成为了可能,而互联网行业,对这类需求更是非常看重,恨不能所有的机器都必须是冗余的,可随意替换的。这样才能保证7*24小时的正常服务。这无疑增加了复杂度的因素。

3.      Scale out的问题: 单机总是有瓶颈的,于是,人们的追求就一定是:不管任何一种角色的机器,都应该可以通过简单的增加新机器的方式来提升整个集群中任何一个角色的性能,容量等指标。这也是互联网行业的不懈追求。

4.      性能:更快的响应速度,更低的延迟,就是更好的用户体验。(所以google用了个“可怜”到家的简单input框来提升用户体验,笑)。

 

说道这里,大概大家都应该对在分布式场景下的广大人民群众的目标有了一个粗略的认识了。

那么我们来看一下原有ACID的问题吧。

 

在上次的章节中,我们也提到了ACID中,A和D相对的,比较容易达到。但C和I都涉及到锁实现,也就和性能紧密的相关了。

然后,人们就开始了纠结,发掘这个C和I,似乎不是那么容易了。

上次,我们谈到,目前主流的实现一次更新大量数据的时候,不同人(或机器)修改数据相互之间不会打架的方法有以下几种:

1.      排他锁

2.      读写锁

3.      Copy-on-write

4.      队列

5.      内存事务

 

 

排他锁和读写锁,本身都是锁的实现,单机的锁实现,相对而言是非常简单的事情,但如果涉及到分布式锁,那么消耗就很高了,原因是,锁要在两边都达到一致,需要多次机器之间的交互过程,这个交互的过程,再考虑到延迟的因素,基本上一次加锁请求就要100~200+毫秒的时间了,那么去锁又要这样的时间。而要知道,我们在单机做内存锁操作,最慢也不过10毫秒。。

于是,有一批人就说了,既然这么难,我们不做了!~来个理论证明他很难就行了~。于是就有了CAP和BASE.

所谓CAP,我个人的理解是描述了一种: 在数据存了多份的前提下,一致性和响应时间,读写可用性不可兼得的“现象”而已。

在我这里来看CAP的证明过程就是个扯淡的玩意儿,他只是描述了一种现象而已。原因还是网络延迟,因为延迟,所以如果要做到数据同时出现或消失,那么按照锁的方式原来可能只需要10ms以内完成的操作,现在要200~400ms才能完成,那自然不能接受了。所谓CAP就是这个现象的英文简称,笑。

 

BASE呢,这个理论似乎更老,其实也是个现象,就是基本可用,软状态,最终一致的简称,也没个证明,其实就是告诉咱:要权衡一下,原来的ACID不太容易实现啦,我们得适当放弃一些啦。但请各位注意,ACID实际上是能够指导我们在什么情况下做什么样的事情能够获取什么样的结果的。而BASE则不行,这也说明BASE不是个经典的理论。

 

好啦。废话了这么多,其实就是想说,分布式场景没有银弹啦,你们自己权衡去吧。我们大牛们救不了你们啦的意思。。

 

既然大牛救不了咱,咱就只能自救了。。。

 

好,好的文章就要在关键的地方恰然而止,留下悬念,我们也就在这里留下点悬念吧。

在这篇中,主要是想给大家介绍一下,目前在分布式场景中,事务碰到了什么问题,出现这些问题的原因是什么。

 

在下一篇中,我将尝试从原理的角度,去分析目前的几类常见的在分布式场景中完成原有事务需求的方法。敬请期待 : ) 


http://qing.weibo.com/1765738567/693f0847330007ay.html下一篇



Dec 07

海量存储系列之六


抱歉大家,间隔有点久,因为这一章要比较细致的总结,所以有些时间耽误。上次我们讲到,单机事务个我们面临的问题,下面我们来说一些我所知的解决的方法。

 

在我开始做淘宝数据层的时候,被问得最多的无非也就是:如何做事务,如何做join.至今仍然如此,我一般都会简单而明确的跟对方说:没有高效的实现方法。

 

虽然没有高效的实现,但实现还是有的。作为引子,我们先来介绍一下这种实现的方式。

 

我们仍然以上一次讲到的bob和smith为例子来说明好了。

开始的时候。Bob要给smith100块,那么实际上事务中要做的事情是

事务开始时查询bob有多少钱。如果有足够多的钱让bob的账户 -100 ,然后给smith 的账户+100 。最后事务结束。

 

如果这个事情在单机,那么事情可以使用锁的方式加以解决。

但如果bob在一台机器,smith在另外一台

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值