最近这段时间,我花了很多时间来更好的理解Hekaton——SQL Sever 2014里的全新内存表技术。我看了很多文章,了解了Haktaon的各种内部数据存储结构(主要是哈希索引和Bw-tree)。另外我也看了不少关于这方面的讲座。
但不止一次,有很多的误报,神话和误解出现,人们对Hektaton的认识发生了错误。从大家对Hekaton的提问就可以看出,我们需要整理Hekaton的知识,向大家重新传达它的相关知识,让大家更好的理解Hekaton,在Hekaton合适的场景来更好的使用它。
下面只是一些我罗列听到的大家关于Hekaton的问题:
这只是在过去我听到的误解中,头条的一部分。因此这篇文章的目的澄清这些最大误解和问题,我也会告诉你为什么它们是错误的。嗯,让我们从我的最头条开始(没特定顺序)!
当我开始讨论Hekaton时,这个总是我第一个想澄清的:当你使用Hekaton时,对于你的事务,它还是有ACID属性的!Hekaton里的事务一直是有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)的。SQL Server只是内部使用不同的概念和方法来保证这4个重要属性。
当你使用只有架构,不包含数据(DURABILITY = SCHEMA_ONLY)的Hekaton表时,你的Hekaton事务的持久性就没有了:当你的SQL Server崩溃了,或者你重启SQL Server,在Hekaton表里的所有数据会丢失。因此没有持久性。因此在事务日志里也没有日志记录。当然这个用法只在特殊场景里使用才有意义,例如为你的数据仓库进行数据加载。如果SQL Server崩溃,你就要重启你的ETL进程。你是可以重建你丢失的数据。
这个误解也非常有意思。有人想说服我说Hekaton是微软开发的No-SQL的新方法。得了吧,使用Hekaton我们还是在讨论有所有ACID属性的关系数据库(参阅刚才说的)。Hekaton和No-SQL是2个完全不同的东西,也没有共同点。Hekaton内部使用优雅而快速的方法来实现关系数据库的特性——就这样!
哇哦,我想我站错了地方!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时,这个神话倒是真的!
理论上这句话是对的。这句话可以从不同方面辩论。我们从第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操作上的基本理解。如果我对此的理解是错误的,欢迎随时纠正我!
这句也非常有意思。我没有在吹嘘这句话!为什么?因为在你的SQL Server数据库里,序列(Sequences)是一个共享对象,意味着访问的当前值是由SQL Server同步的。这个同步在竞争中结束,意味着你不能延伸你的工作量,Hekaton的一切都是延伸工作量。
我用序列值在CTP1上做过一些测试,一旦你在你的Hekaton表/存储过程上执行大量的并行线程,你就会触发序列生成器(Sequence Generator)里的竞争。当然在一些内部页,序列生成器(Sequence Generator)存储着当前值,当序列生成器读写这些特定页时,就会发生闩锁(latch) 。在序列生成器里,你就用闩锁竞争(Latch Contention)结束它了,你的Hekaton工作量也不会延伸。在我的测试里也没太大区别,如果我请求整个范围的序列值,或者当我使用缓存,也没有区别。序列生成器始终是瓶颈。
那你如何克服这个特定问题?使用老好朋友UNIQUEIDENTIFIER。这些值彼此间是完全独立生成的,意味着当你生成新值时,没有涉及到共享资源,因此你可以剔除这个瓶颈,直到你触发CPU 的100%的使用率(包括像事务日志,网络带宽等其他瓶颈)前,Hekaton的工作量是可以不断延伸的。
这句话是对的,只要你对数据库设计没想法。我刚才已提过,在第1个发布里不能创建外键(Foreign-Keys)来检查约束。我从没看过任何基于磁盘的表,可以逐个迁移到内存优化表(Memory-Optimized table)。还记得么,INT IDENTITY 值目前尚不支持。当你迁移到Hekaton时,你做的不只是简单的切换来获得100倍的性能提升。抱歉!
这句话是对的,只要你对数据库设计没想法。我刚才已提过,在第1个发布里不能创建外键(Foreign-Keys)来检查约束。我从没看过任何基于磁盘的表,可以逐个迁移到内存优化表(Memory-Optimized table)。还记得么,INT IDENTITY 值目前尚不支持。当你迁移到Hekaton时,你做的不只是简单的切换来获得100倍的性能提升。抱歉!
错!范围索引使用所谓的Bw-Tree,这个当前SQL Server为聚集和非聚集索引使用的B+树结构几乎一样。Bw-tree是基于B-Link tree——大家可能有点迷糊了,和传统的B+树相比,Bw-Tree有3个重大不同:
从微软观点来说这个是对的,因为自SQL Server 2012起SQL Server是在CPU核心层授权的。你用的CPU周期越多,需要的CPU就越多,你付给微软的授权就越多。但从架构观点来说是错的!数据库处理的是存储和获取数据,但是数据库不是一个应用服务器,应用服务器才处理业务……想想看。当你有CPU竞争时,因为你在数据库服务器里运行大量的业务逻辑,你应该重构你的数据库,因此你把你的业务逻辑移向专用的应用服务器,作为SQL Server的授权完全不一样——你为操作系统付钱,这就是错的原因!
当你想把整个数据库迁移到Hekaton时,请先好好想想。Hekaton是用来解决特定问题的,像闩锁竞争(Latch Contention)。只有把特定的表和存储过程迁移到Hekaton时才有意义——并不是“所有”的数据库!对于每个数据库对象(表,存储过程),SQL Server需要把它们编译和链接成对应的DLL文件(然后载入sqlservr.exe的运行空间,这样要花费时间)。当你重启你的SQL Server时或者进行故障群集转移时,也会执行编译和链接。这会直接影响你HA方式的目标恢复时间(Recovery Time Objective,RTO)。
我希望已经帮你澄清了Hekaton的一些神话,误报和误解。而且我一直强调的是:如果是“对的”问题,Hekaton可以帮解决;如果你有传统的问题(错误的索引设计,糟糕的存储性能)果断放弃Hekaton吧,先做好你的家庭作业先。
Hekaton就像F1赛车:
感谢关注,期待您的留言!