领域驱动设计之SQL语句要不要写?

栏目: 数据库 · 发布时间: 6年前

内容简介:无论我们采取以内存中模型对象为主,还是以数据库为主,都离不开SQL语句,只是SQL份量轻重不同而已。使用SQL最主要好处是数据库的ACID事务机制,所以,如果我们无法解决在应用服务层实现事务,甚至分布式事务问题之前,还是需要JTA结合事务中间件帮助我们解决数据可靠性的问题。

板桥banq

目录

  1. 领域的定义
  2. 如何编写类?
  3. 对象如何创建
  4. 对象模型的仓库
  5. SQL语句要不要写

第五章

无论我们采取以内存中模型对象为主,还是以数据库为主,都离不开 SQL 语句,只是SQL份量轻重不同而已。

使用SQL最主要好处是数据库的ACID事务机制,所以,如果我们无法解决在应用服务层实现事务,甚至分布式事务问题之前,还是需要JTA结合事务中间件帮助我们解决数据可靠性的问题。

前面我们讨论了实体状态修改,状态的修改如果是在内存中进行,则是由多线程完成的,多个线程或说多个操作修改一个状态,必然引起对状态资源争夺问题,同时修改肯定是不可能的了,使用锁这又会引起性能问题。状态修改如果是使用数据库事务,同样也会引起性能瓶颈,如何在性能和事务之间平衡?

事件驱动状态改变

那么有没有两全其美,可以采取用事件替代状态,因为事件就是状态切换的意思,状态为什么会变?因为事件发生了,比如你收到短信银行卡余额为零,你就进入没钱状态了。

之前我们纠结于如何保存状态,保存状态不但有内存和数据库两处同步一致性问题,就是在内存中或是在数据库字段中,还有并发读写问题,数据库是采取ACID机制解决的,A是原子性,保证多个步骤如一个步骤,D是持久保存性,C是一致性,保证有相互关联的几处数据同时一致修改,I是隔离性,最好的隔离性是完全隔离,每个操作都完全锁住数据表字段,其他操作必须等待,实现串行化操作,好比几个人并排走到小胡同前,只能一个个串行通过,这种操作并发性很差,所以性能也很差。状态在内存中修改也存在这种并发问题,使用同步锁等堵塞操作,某个时刻只能有一个操作修改状态,性能很差。不过 Java 语言生态发展快,无锁非堵塞并发技术很多,可选择范围大,所以,如果你把状态放在实体对象中,以修改实体对象状态为主,修改完以后,将修改事件发给数据库再修改,这是不是类似数据库的主从架构呢?总之腾挪余地很大。

但是如果以修改数据库状态为主,再同步到内存缓存中的实体对象,那么在修改状态这个环节,你还是使用传统数据库事务机制,性能受制于数据库,如果你的应用修改等写操作不大,大部分只是读取查询操作可以这么做,当然我们这里谈的状态改变,肯定属于写操作,这里也看到,DDD是适合写操作环节,大量读操作,报表统计等可不必教条遵循DDD去建模。

写操作使得状态改变,这些写操作就是事件概念,如果我们不保存状态,只保存写操作事件,形成一个事件集合,时间序列上的事件数据库,当前状态的获得只要读取事件数据库,再重新运行这些历史事件就能获得当前状态了,这样把状态的改变其实从写操作转移到读操作,写操作中只要把事件追加到数据库或磁盘文件后面,没有修改,只有新增追加,性能会很高,而且如果排队追加,保证顺序性,等同于实现了ACID中的串行化,完全满足ACID四个特性,每次只追加一个事件本身就是原子性,一致性通过时间前后顺序保证,隔离性是串行化操作,持久性则是因为追加到磁盘文件或事件日志,都是落地在磁盘上的,这其实也是关系数据库内部事件日志的工作原理,只不过我们从数据库这个Box中取出在应用层实现了。这就是事件溯源Event Sourcing基本原理,带来问题是复杂性,这如同模块化初期带来复杂化,因为组件数量增加,微服务也带来复杂性,因为服务数量增加,对象化也带来复杂性,因为对象数量增加了。p

CAP定理

如果我们采取保存事件,而不是直接保存状态,那么我们就有可能实现分布式事务。

传统分布式事务主要是2PC,两段提交方式,主要由事务中间件完成,比如Weblogic和Oracle的卖点之一是其分布式事务中间件技术,从服务的事务最终到集群的数据库事务,都有一套严格的事务机制,比如Oracle RAC方式下,通过配置Weblogic的Grid数据源能保证Weblogic事务到Oracle数据库事务的亲和性,这些都是很微妙的微调技术,但是使用这样的JTA+XA技术问题也有,比如会和Dblink冲突等,数据库需要等待比较长的Jta方法执行过程,保持这个过程中所有参与资源的可管理性,数据库也很累,硬件要求提高了,又要用钱了。

如果我们在程序设计方面多做点事情,就可更巧妙地实现分布式事务,这需要把我们思路从事务上面提高到一个更高层次,那就是看看分布式系统的CAP定理,C代表高一致性,A代表高可用性,低延迟,高性能,P代表分区容忍性,本来一直同步的两个机器如果中间通讯断了或网路抖动,能不能各自活下去?如果有一方一旦发现网络抖动,因为它当局者迷,搞不清是网络抖动还是对方当机了,那么是否能够继续运行下去,如果继续运行下去,表示容忍这种分区断裂联系,如果发现不能与对方通讯了,就拼命重连,主要工作就是疯狂重连,自己本来干的活都停下来等重试成功再进行,这是一种无法容忍分区的表现,疯狂重连是因为它要和对方同步状态,把双方状态一致性看成最重要的,结果这种疯狂行为看似符合逻辑,最终命运不是它可以主宰的,因为它选择了CAP中的CP,所以它其实丧失了可用性,甚至会死机,这就是疯狂不断同步的后果,如果我们设置重试次数以后,直接失败,这样可用性会提高,当然问题是,两边状态不同怎么办?这个你不用焦虑了,接受最终一致性吧。

如果我们有一种机制,重试以后放弃,然后在下次对方苏醒过来,不论是网络正常还是对方机器正常,都能把失败之前的数据继续同步过去,不就是实现了最终一致性了吗?

这就是Kafka这种日志型消息系统提供的机制,说白了就是保证消息传递的原子性,保证准确同步,就是失败,只要恢复正常,可从原来断开的地方继续进行。

分布式事务

如果我们把状态的写操作事件作为消息通过Kafka这样的同步 工具 准确送达到对方,对方把事件作为同一套代码生成的模型对象的方法中,等于零再次播放事件,不就获得了当前状态吗?也就是说,不但自己可以通过事件播放获得当前状态,也可把事件以顺序性广播出去,其他机器播放事件也能获得当前状态,当然关键问题是,如何保证其他机器上获得的事件顺序和原机器上是一致的,通过时间 排序 也不行,因为不同服务器之间时钟肯定无法100%同步。这个问题解决了,分布式事务问题其实就会解决,没有通用完美方案,只有适合不同上下文的合适方案。

比如如果是转账案例,一个人账户有两个转账事件,如果两个都是入账事件,那么前后顺序对于账户余额没有影响,但是如果是一个是扣款,一个是入账,那么前后顺序就有影响了。这是一种可交换性的案例吧。

另外一个思路从时钟上入手,实际时间很难同步,使用逻辑时钟吧,或用全局ID序列产生器,产生唯一ID,再根据ID排序。

小结

说了这么多,使用SQL是为了利用其数据库的事务机制,这是一般做法,在服务中引入JTA,然后配置XA数据源,这样服务中涉及的资源包括数据库或JMS都在事务两段式照顾下,一般应用没有问题,但是应对大级别访问会加重数据库负载,这时我们提出了新的解决方案,通过事件溯源来解决分布式事务,提高了性能和吞吐量,也能实现最终一致性的柔性事务,有兴趣可了解Saga模式。


以上所述就是小编给大家介绍的《领域驱动设计之SQL语句要不要写?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Trading and Exchanges

Trading and Exchanges

Larry Harris / Oxford University Press, USA / 2002-10-24 / USD 95.00

This book is about trading, the people who trade securities and contracts, the marketplaces where they trade, and the rules that govern it. Readers will learn about investors, brokers, dealers, arbit......一起来看看 《Trading and Exchanges》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器