你的位置:首页 > 数据库

[数据库]Hekaton的神话与误解


最近这段时间,我花了很多时间来更好的理解Hekaton——SQL Sever 2014里的全新内存表技术。我看了很多文章,了解了Haktaon的各种内部数据存储结构(主要是哈希索引和Bw-tree)。另外我也看了不少关于这方面的讲座。

但不止一次,有很多的误报,神话和误解出现,人们对Hektaton的认识发生了错误。从大家对Hekaton的提问就可以看出,我们需要整理Hekaton的知识,向大家重新传达它的相关知识,让大家更好的理解Hekaton,在Hekaton合适的场景来更好的使用它。

下面只是一些我罗列听到的大家关于Hekaton的问题:

  • “Hekaton是内存存储技术,是否意味着数据不再永驻?”
  • “Hekaton只在特定架构的CPU里可以运行?”
  • “当你迁移到Hekaton,对于你的工作量,你会获得100倍的性能提升?”
  • “在Hekaton里,没有锁,阻塞,自旋锁。”

这只是在过去我听到的误解中,头条的一部分。因此这篇文章的目的澄清这些最大误解和问题,我也会告诉你为什么它们是错误的。嗯,让我们从我的最头条开始(没特定顺序)!

“对于事务,Hekaton也提供ACID属性么?”

当我开始讨论Hekaton时,这个总是我第一个想澄清的:当你使用Hekaton时,对于你的事务,它还是有ACID属性的!Hekaton里的事务一直是有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)的。SQL Server只是内部使用不同的概念和方法来保证这4个重要属性。

  • 原子性(Atomicity):对于你处理的事务,还是可以前滚(rolled forward)和后滚(rolled back),即使你的SQL Server崩溃,对于你启用Hekaton的数据库,SQL Server还是可以通过故障修复(crash recovery )(只要通过SQL Server重启,你的数据还是持续的(persisting )——下面会详细描述)
  • 一致性(Consistency):Hekaton也提供“总是”有一致的数据。这个在当前很容易做到,因为Hekaton的最大局限性之一是不能创建外键(Foreign-Keys),只能本身约束。因此基本上启用Hekaton的表,是在内部进行约束的。
  • 隔离性(Isolation):几天前,有人想说服我,Hekaton提供你脏读(Dirty Reads),因为没有锁和阻塞(Locking and Blocking)。错了!!!Hekaton使用所谓的多版本并发控制(Multi Version Concurrency Control ,MVCC)的方法,才有没锁的神奇可能。当你读取数据的时候,你取回的是在你开始的语句/事务后不在有效的数据(取决于使用的事务隔离级别(Transaction Isolation Level))。因此在Hekaton里没有脏读(Dirty Reads)。
  • 持久性(Durability):这个要分情况。当你使用架构和数据(DURABILITY=SCHEMA_AND_DATA)持久性创建你的Hekaton表,在SQL Server崩溃时,数据还是存在的。它会从事务日志里恢复,即Hekaton写的检查点文件。这点非常重要:当你想要你的数据持久时,Hekaton还是会使用事务日志,这也意味着你的事务日志是Hekaton的最后性能瓶颈之一。当和传统基于传统硬盘的表相比,Hekaton这里使用非常高效的日志模型。这里其中一个改变是,只有数据修改被日志,不是在索引级别。当你在基于传统硬盘表里执行INSERT,SQL Server需要为“每个”索引(聚集和非聚集索引)的INSERT日志。在Hekaton里,SQL Server只日志一次INSERT,因为在SQL Server启动期间,所有的Hekaton索引(哈希索引,范围索引)都会重建。因此对事务日志的影响是尽可能小的。

当你使用只有架构,不包含数据(DURABILITY = SCHEMA_ONLY)的Hekaton表时,你的Hekaton事务的持久性就没有了:当你的SQL Server崩溃了,或者你重启SQL Server,在Hekaton表里的所有数据会丢失。因此没有持久性。因此在事务日志里也没有日志记录。当然这个用法只在特殊场景里使用才有意义,例如为你的数据仓库进行数据加载。如果SQL Server崩溃,你就要重启你的ETL进程。你是可以重建你丢失的数据。

“Hekaton是微软提供的No-SQL方法?”

这个误解也非常有意思。有人想说服我说Hekaton是微软开发的No-SQL的新方法。得了吧,使用Hekaton我们还是在讨论有所有ACID属性的关系数据库(参阅刚才说的)。Hekaton和No-SQL是2个完全不同的东西,也没有共同点。Hekaton内部使用优雅而快速的方法来实现关系数据库的特性——就这样!

“Hekaton只在特定架构的CPU上运行?”

哇哦,我想我站错了地方!Hekaton只在特定模型/架构上运行,因为Hekaton内部使用所谓的原子CAS运算(原子比较与交换Atomic Compare & Swap, or Atomic Compare & Exchange)。那句话是完全错误的!当然,内部的Bw-tree使用CAS运算作为多个原子步骤做出树里SMOs(结构修改运算,Structure Modification Operations)。Hekaton这里内部使用“InterlockedCompareExchange”的WIN32 API函数。这个函数只在特定内存位置的值和原始值比较,如果2个一样的话,在内存位置会写入新值。函数本身在CPU级别作为一个原子汇编指令执行,意味着没有别的线程可以干涉那个汇编函数。它作为一个原子块(atomic block)从开始到结束执行。

这里的神话是,需要的汇编函数只在特定CPU架构上支持。这没错,但是这个汇编函数从奔腾处理器开始就支持了!在386和486架构上,函数本身是不支持的……从刚才提到的MSDN文章里的要求部分看到,支持的最低系统版本是Windows XP!因此当你在前Windows XP系统里安装SQL Server 2014时,这个神话倒是真的!

“在Hekaton里,没有锁,阻塞,自旋锁。” 

理论上这句话是对的。这句话可以从不同方面辩论。我们从第1个方面开始。Hekaton“本身”是没有锁,阻塞和自旋锁的,但是你还是和SQL Server的传统关系引擎打交道。这就意味着,当你离开Hekaton宇宙时,你还是和基于原始代码的SQL Server打交道(对此,我表示遗憾……),例如事务日志管理器( Transaction Log Manager)。这些代码还是有闩锁(latches)和自旋锁(spinlocks)用来保持不同线程访问的同步。从这个角度来说,上述语句是部分正确的。 

第2个方面在Hekaton里你还是有阻塞(blocking)的地方是,当你进行原子CAS操作时,一个原子CAS操作不能被不同的线程中断。因此在Bw-Trees里SMOs(结构修改运算,Structure Modification Operations)可以以聪明、优雅的方式实现。着也意味着当你想在同个Bw-Tree里的同个页里同时执行一个SMO是,一个线程会胜出,其他的线程需要重试原子CAS操作。同时发生了什么呢?线程会旋转,再次尝试CAS操作。我的基本理解是,原子CAS操作本身是就像一个Criticial Section同步概念包装的汇编函数。这就意味着你的线程需要旋转,你在丢失CPU周期,并增加了你事务的闭锁性。当然,SMOs应该非常非常少见,因此这没什么大不了的——但还是有线程旋转的可能,当有为底层同步对象(或者汇编函数)的竞争时。 

这是我在原子CAS操作上的基本理解。如果我对此的理解是错误的,欢迎随时纠正我! 

“因为在Hekaton里不支持INT IDENTITY值,使用序列(Sequences)?” 

这句也非常有意思。我没有在吹嘘这句话!为什么?因为在你的SQL Server数据库里,序列(Sequences)是一个共享对象,意味着访问的当前值是由SQL Server同步的。这个同步在竞争中结束,意味着你不能延伸你的工作量,Hekaton的一切都是延伸工作量。 

我用序列值在CTP1上做过一些测试,一旦你在你的Hekaton表/存储过程上执行大量的并行线程,你就会触发序列生成器(Sequence Generator)里的竞争。当然在一些内部页,序列生成器(Sequence Generator)存储着当前值,当序列生成器读写这些特定页时,就会发生闩锁(latch) 。在序列生成器里,你就用闩锁竞争(Latch Contention)结束它了,你的Hekaton工作量也不会延伸。在我的测试里也没太大区别,如果我请求整个范围的序列值,或者当我使用缓存,也没有区别。序列生成器始终是瓶颈。

那你如何克服这个特定问题?使用老好朋友UNIQUEIDENTIFIER。这些值彼此间是完全独立生成的,意味着当你生成新值时,没有涉及到共享资源,因此你可以剔除这个瓶颈,直到你触发CPU 的100%的使用率(包括像事务日志,网络带宽等其他瓶颈)前,Hekaton的工作量是可以不断延伸的。 

“对于你的程序,Hekaton是完全透明的(is completely transparent)。”

这句话是对的,只要你对数据库设计没想法。我刚才已提过,在第1个发布里不能创建外键(Foreign-Keys)来检查约束。我从没看过任何基于磁盘的表,可以逐个迁移到内存优化表(Memory-Optimized table)。还记得么,INT IDENTITY 值目前尚不支持。当你迁移到Hekaton时,你做的不只是简单的切换来获得100倍的性能提升。抱歉! 

“对于你的程序,Hekaton是完全透明的。”

这句话是对的,只要你对数据库设计没想法。我刚才已提过,在第1个发布里不能创建外键(Foreign-Keys)来检查约束。我从没看过任何基于磁盘的表,可以逐个迁移到内存优化表(Memory-Optimized table)。还记得么,INT IDENTITY 值目前尚不支持。当你迁移到Hekaton时,你做的不只是简单的切换来获得100倍的性能提升。抱歉!

“对于范围索引(Range Indexes),Hekaton使用传统的B+树结构。” 

错!范围索引使用所谓的Bw-Tree,这个当前SQL Server为聚集和非聚集索引使用的B+树结构几乎一样。Bw-tree是基于B-Link tree——大家可能有点迷糊了,和传统的B+树相比,Bw-Tree有3个重大不同: 

  • 中间层的页存储大范围的键值,在下一层的页存储小范围的键值。因为在页上存储了大范围的键值,Smos(例如页分裂)可以在2个原子操作实现(通过2个原子CAS执行)。这个概念来自于B-Link tree的设计原则。
  • 页“从不”改变,因此这个会导致CPU缓存线无效,这个会穿越传播到整个内存架构,这是非常昂贵的(在浪费CPU周期这方面)。当Hekaton需要改变内存中的页,不会接触到原始内存位置,Hekaton只创建一个新的Delta记录,这就是修改操作。所谓的”页面映射表(Page Mapping Table)“指向新的Delta记录,Delta记录对应的原始记录并未修改。因为这个方式CPU缓存线无效可以避免。
  • 页大小是弹性的,并不是一直的8kb大小。 

“在数据库里,Hekaton提供你超快的业务逻辑。”

从微软观点来说这个是对的,因为自SQL Server 2012起SQL Server是在CPU核心层授权的。你用的CPU周期越多,需要的CPU就越多,你付给微软的授权就越多。但从架构观点来说是错的!数据库处理的是存储和获取数据,但是数据库不是一个应用服务器,应用服务器才处理业务……想想看。当你有CPU竞争时,因为你在数据库服务器里运行大量的业务逻辑,你应该重构你的数据库,因此你把你的业务逻辑移向专用的应用服务器,作为SQL Server的授权完全不一样——你为操作系统付钱,这就是错的原因!

“我如何迁移我的整个SAP数据库到Hekaton。微软对此有提供工具么?”

当你想把整个数据库迁移到Hekaton时,请先好好想想。Hekaton是用来解决特定问题的,像闩锁竞争(Latch Contention)。只有把特定的表和存储过程迁移到Hekaton时才有意义——并不是“所有”的数据库!对于每个数据库对象(表,存储过程),SQL Server需要把它们编译和链接成对应的DLL文件(然后载入sqlservr.exe的运行空间,这样要花费时间)。当你重启你的SQL Server时或者进行故障群集转移时,也会执行编译和链接。这会直接影响你HA方式的目标恢复时间(Recovery Time Objective,RTO)。

小结

 我希望已经帮你澄清了Hekaton的一些神话,误报和误解。而且我一直强调的是:如果是“对的”问题,Hekaton可以帮解决;如果你有传统的问题(错误的索引设计,糟糕的存储性能)果断放弃Hekaton吧,先做好你的家庭作业先。

Hekaton就像F1赛车:

你技术不好的话,F1赛车也帮不了你!

感谢关注,期待您的留言!