作为国民经济的命脉和枢纽,金融行业对底层数据库的能力要求正在不断提高。在众多要求中,数据一致性无疑是重中之重,即数据不能出错,最好还能提高并发效率。
TDSQL采用MC(轻量级GTM)+全局MVCC的全局读一致性方案。如果只使用全局事务管理器GTM,除需维护全局序列外,还需要维护全局的事务冲突,这个过程的通信量及与GTM之间的通信频率都会成为瓶颈。TDSQL引入全局MVCC,将每个分片上MVCC版本和全局GTS做映射,通过全局GTS和全局的MVCC映射来管理每个分片上的镜像,进而实现全局的MVCC,从而极大减少和GTM 之间的通信量及避免全局的冲突事务检测。
上述方案确保了TDSQL无任何数据异常,且具备高性能的可扩展性,解决了分布式数据库在金融级场景应用的最核心技术挑战,使得国产分布式数据库实现在金融核心系统场景的可用、好用,进而推动国产基础软件产业化。基于此,TDSQL是当前国内率先进入国有大型银行核心系统正式投产的国产分布式数据库。
在WOT全球技术创新大会2022的腾讯云数据库专场中,腾讯云数据库专家架构师汪泗龙分享了金融级分布式数据库TDSQL的一致性技术及应用实践,以下为详情内容:
腾讯云数据库TDSQL诞生自腾讯内部百亿级账户规模的金融级场景。从内部自研蜕变成规模化商业产品,TDSQL的发展历程可分为四个阶段:
第一阶段是2007-2009年,当时开源的MySQL已经越来越难以应对腾讯爆发式增长的业务,研制服务于计费、定位于金融场景的分布式数据库TDSQL逐渐提上日程。
第二阶段是2009-2012年,腾讯进入开放时代,海量业务群雄并起,以开心农场等为代表的众多亿级应用比比皆是。TDSQL逐渐在性能瓶颈、数据可靠性保障、高可用等“不可能三角”的技术难题上取得突破。
第三阶段是2012-2014年,云计算兴起,数据库上云、多租户、标准化成为标配。腾讯云数据库的能力逐渐外溢,TDSQL因其优异的性能已经拥有众多外部客户。在经过公有云的海量数字化、大规模高并发业务场景打磨以及内核级的深度自研优化后,TDSQL逐渐形成标准化的国产分布式数据库产品,包括金融级分布式TDSQL、计算与存储分离的云原生数据库TDSQL-C 等产品,获得了云原生技术、多租户隔离能力。
第四阶段是2014-2020年,数字化升级成为行业趋势,TDSQL深入金融核心,走向大规模应用阶段,比如作为微众银行分布式数据库底座承担核心作用,帮助张家港农商银行上线新一代核心业务系统,助力平安银行打造信用卡“A+”新核心系统等等。

目前,TDSQL已服务金融、政务、工业制造等行业超过50万家客户,帮助20余家金融机构完成核心替换,国内TOP 10银行机构服务占比超过60%。同时TDSQL也支持了第七次全国人口普查,以及腾讯会议、健康码等关系国计民生的数字化应用,有力推进国产数据库的技术创新和发展。
随着云计算和数字化业务的发展,针对新型企业级信息化以及实现国产化的转型升级需求,TDSQL逐渐形成6大核心特性:
TDSQL的架构主要分为三层。最上层是管理层,包含赤兔管理平台、MC以及Keeper。中间层是SQL引擎,可以选择使用TDSQL附带的接入层,也可使用传统意义上的F5等接入层来进行接入。最下层是存储引擎层,即TDSQL内核。

下图是实现 GTM 全局唯一序列的图签。这里是TDSQL在实现分布式事务后在全局一致性读方面的优化点,该插件实现了轻量级的GTM。

事务处理面临的挑战有两点:保障数据正确性及提高并发效率。这两点都是事务处理的关键点,如果为了提高并发效率而牺牲数据正确性,就背离了初衷,反之也是如此。为了解决这两个问题,我们需要用到很多技术,比如读写分离、物理时钟、时间戳机制、GTM等。

不管是分布式数据库还是应用,都是在分布式事务模型下,进行分布式事务的实践和开发。对数据库而言,常用的分布式事务模型是XA模型;对应用而言,主要是TCC、SAGA、AT等模型。

Oracle、MySQL、TDSQL等数据库,通常使用XA模型来进行分布式事务处理。在XA模型里,事务主要采用两阶段提交方式,先进行PREPARE,再进行COMMIT,也称之为两阶段事务。

事务模型简化后划分为读写两类操作,组合下来有四种场景,即读写、写读、读读、写写。存在冲突(数据异常)的场景主要是写写、写读、读写,具体的冲突如下:

针对上述问题,我们可以通过数据库的并发控制算法来解决:

我们需要根据不同的场景来使用不同的算法。在分布式事务处理方面,TDSQL使用的是2PL加上MVCC特性。
我们以下图为例说明全局读不一致问题。有A、B两个账户,账户余额均为100元。有两个事务,事务X和事务Y,事务X是A给B转100元,事务Y是读取A和B的余额。当事务Y发起查询时,事务X中(NA分片属于COMMITting状态,NB分片属于COMMITed状态),这时读到两人余额总和为300元,这就是分布式事务进行过程中的全局读不一致问题。

出现这种全局读不一致的情况,主要原因在于这里没有全局一致的MVCC版本,而是依赖每个分片各自的MVCC特征进行实现,我们读到的NA片还没提交,因此读到的数据是不一致的数据。针对上述异常情况,主要有以下几种解决方案:

TDSQL的全局读一致性方案是MC(轻量级GTM)+全局MVCC。只使用全局事务管理器GTM的方案,除需维护全局序列外,还需要维护全局的事务冲突,这个过程的通信量及与GTM之间的通信频率都会成为瓶颈。因此需要引入另一个特性——全局MVCC,这时的读写冲突可通过undo的前镜像完成一个全局的MVCC来实现,解决各副本间的读写冲突。
完全重新开发一套全局MVCC的成本较高,且和InnoDB的兼容性是个问题。因此我们采用了折中方式,我们将每个分片上MVCC版本和全局GTS做映射,通过全局GTS和全局的MVCC映射来管理每个分片上的镜像,进而实现全局的MVCC。这样就可极大减少和GTM 之间的通信量及避免全局的冲突事务检测。
下图为TDSQL分布式事务的实现模型,主要有三个角色:

通过Client发起一个事务BEGIN后,会往后端发起一个插入语句到Proxy,此时Proxy发起XA Start,到后端的两个SET上,两个SET返回正常后则插入成功。获取到XID后,开始正式进入COMMIT阶段。Client发起COMMIT后,Proxy往后端发时就会带着全局的XID往后提交,这时进入PREPARE和COMMIT阶段。
当所有参与者PREPARE成功后,会插入全局的XID_LOG。XID_LOG是PREPARE阶段的一个平衡点,解决悬挂等待的高成本及超时回滚等问题。一旦事务进入到这个阶段,写入全局的XID_LOG成功后,即使后面的操作失败,我们依然会认为事务是成功的。针对没有提交成功的事务,Agent参与进来,扫描XID_LOG表进行后续处理;如果执行超时,Agent会参与进来,终止掉超时的事务,之后Agent会更新XID_LOG的状态。
Proxy和Agent是协作模式,XID同一时间点只能有一种状态,我们可以通过这种状态来协调Proxy和Agent。如果Proxy Crash掉,Agent进行任务接管,根据XID_LOG的信息决定对事务进行补提交或回滚。在此过程中,Proxy本身是无状态的,这就使得TDSQL具备良好的业务体验。
如果在主备过程中,一个SET发生主备切换,另外一个SET正常,这时Agent会根据全局XID_LOG的状态进行相关补偿或者回滚。如果XID_LOG写成功,我们会进入到最终的COMMIT阶段,两个分片分别COMMIT成功后,会正常反馈给Client端。以上就是TDSQL基于XA两阶段提交的分布式事务模型和原理。
分布式数据库,不仅要解决分布式事务的问题,还要解决全局写读冲突,从而实现全局的一致性读。TDSQL具体的实现方案如图所示:

与前面提到的TDSQL分布式事务模型相比,整体架构较为相似,区别在于多了一个MC组件。
MC是全局的轻量级GTM,负责生成全局的唯一序列。以上述两个SET的插入的事务模型为例,整个过程中只是多了两次和GTM之间的获取步骤,其他流程是一致的。
当我们发起一个分布式事务,在CLIENT启动及提交时,我们会在这里再加入一个阶段,获取全局最大的GTS。GTS是标识的全局唯一序列,XID是唯一标识全局事务的标签,两者相互独立分别保证分布式场景下读取一致性和事务一致性。在COMMIT发起后进入两阶段提交,这里需要对PREPARE阶段进行判定,通过XID_LOG日志来作为全局提交成功的标识。在PREPARE成功后,PROXY和MC会进行第二次交互,重新获取COMMIT_GTS,并伴随事务的COMMIT写入到TDSQL REDO日志和缓存中。相对于XA的START和COMMIT,TDSQL为了实现全局的MVCC特性,改写了XA的语法,增加部分关键词,同时对内核做微量调整,以上就是TDSQL实现全局一致性读的方案。
在该方案中,我们和MC的通信量非常少,整个过程中基本只有2次非常轻量的通信,某些场景下当我们进行一个不涉及多分片的事务时,即如果只涉及一个分片,我们会对第二次获取COMMIT_GTS进行优化,进一步减少和MC通信。实际上,TDSQL实现分布式事务和分布式全局一致性的方案,是把InnoDB自身的MVCC和全局GTS进行全局映射,从而实现全局轻量级的GTM+全局的MVCC。
下图是InnoDB的MVCC模型,共有六个事务,当前事务ID是trx6,为活跃状态。众所周知,InnoDB会将trxid直接排序,通过全局事务链表管理维护。我们以下图为例来说明可见性算法是如何对外可见的。

我们过滤时需要过滤事务本身的可见性情况,要看哪些事务对外已经能看到属于活跃事务。我们可以过滤掉trx7和trx5,活跃事务为trx8、trx4、trx3。根据活跃的trxid,我们可以获取到两个id,一个是比较旧的快照即up_limit_id;另一个是比较新的快照地址即low_limit_id。如果当前事务的id小于up_limit_id,说明这属于比较老的快照地址,对当前查询来说事务可见;如果当前事务的id大于等于low_limit_id,说明是非常新的快照地址,事务不可见。当处于两者之间时,就需要判断是否为活跃状态。
我们以下图为例,来说明如何把全局可见性视图串起来。有四行记录,分别是ROW0到ROW3,需要查询一个比较老的快照(up_limit_id:76),我们可以看下每一条记录的查询情况。ROW0行有两个版本,trxid:100和trxid:62。ROW1行有trxid:80、trxid:75、id32三个版本。ROW2只有trxid:20一个版本。对于ROW0,满足条件的是trxid:62。对于ROW1,满足条件的是trxid:75和trxid:32,因为我们找快照时是根据列表找到满足条件的第一个快照,而非找到最老的trx:32,要找的是trx75。另外对于ROW2,当前只有一个版本,且只有20,这时已经满足条件。

InnoDB本身是使用MVCC机制来解决读写并发问题,通过Undo log来对应事务中的读写语句。Undo log记录的是每个旧的镜像版本,当事务需要读取旧版本时,可以通过链表去回溯旧的版本。当需要回滚时,也可以基于Undo来进行相关的数据回滚。
TDSQL实现全局MVCC的原理与InnoDB相似,将全局的trxid和全局事务序列对应,将trxid和全局GTS进行关联,从而实现全局的MVCC。如下图所示,全局场景下会存在全局序列GTS。对于trx6,其GTS值为150,对它可见的GTS必须小于等于150,如果比它大则不可见。因此trx7和trx8对其不可见;trx5的GTS是100,小于150,因此可见;trx4是大于100,在100-150之间因此可见;trx3的GTS值是300,也不可见。

对于trx8、trx7、trx4和trx3,我们要分别找到对应记录行的Undo历史版本。如果找到的Undo项的GTS值依然大,就继续往前找。对于trx8关联的记录行通过undo链的回溯,最终找到的记录所绑定的GTS是30,以此类推对于trx7“最终记录行”是70,trx4是40,trx3是20。这样trx6,trx8、trx7、trx5、trx4、trx3对应“可见的记录行”的GTS值分别是30、70、100、40和20。
回到前文流程,这时全局事务还在COMMTTING过程中,一个事务已经提交,另一个事务没有提交,在另外一个读事务扫描记录行的过程中,读取到前两个写事务时,我们可能都要通过undo获取他们的历史版本。TDSQL的全局可见性算法是把节点局部的trxid和全局GTS进行映射关联,实现全局MVCC,从而消除读写冲突和一致性问题。
我们以一个写事务和读事务的场景为例,开启MC后,TDSQL对PREPARE状态进行了相关处理。当写事务进行UPDATE操作后,这个事务可能为ACTIVE、PREPARE或COMMIT状态,此时需要判断这些状态是可见还是不可见。

对于处于ACTIVE状态的事务,InnoDB下对非当前事务不可见,转换为COMMIT状态后,通过比较GTS,再根据全局层面的可见性来进行判定。同样地,一个PREPARE中的事务,对于InnoDB或TDSQL都处于等待状态。如果等的时间久了,则全局的分布式事务效率会降低,需要进行优化。所以每笔请求时,SELECT都会带上GTS,当扫描到这一行时,会比较数据行的GTS和当前SELECT的GTS,如果SELECT的GTS大于数据行的GTS则可见,否则不可见。当数据行处于PREPARE状态时,我们还不知道它提交的GTS,此时可见性无法判断,需要等到进入到提交状态,才能进行可见性判断。
下图所示的场景中,存在两个UPDATE事务,当第一个节点COMMIT之后,另一个节点仍处于PREPARE状态,这时我们需要等它提交及返回结果。这个过程中它的状态不可知,因此无法比较GTS值,则需要等待,不会读取到中间状态。

TDSQL主要从三个方面针对全局一致性读进行设计优化:

下图主要介绍TDSQL在开启MC之后的优化。首先是tlog,tlog实现了本地MVCC和全局MVCC之间的映射关系,主要存储的是GTS和trxid。tlog的整体设计非常简洁,一个tlog里包括4K的数据页,每一页包含4K个字节,一个GTS是64位的无符号整型,占8字节,整个页面是128个字节,128字节中最后的111字节是checksum值。因此每个页面能存储约496个GTS。

第二是trxid和GTS映射。要实现高效的映射,就需要有快速定位的方法。实际上,每个trxid对应到tlog时都有偏移,文件起始的trxid称为startid,当前的trxid减去stardid乘以8,就是偏移值。基于这个算法,我们可以快速定位到文件的地址和偏移值,实现快读定位。

第三是tlog和redo。tlog可实现了全局MVCC和GTS映射,这个过程中也需要对XA语句进行调整。我们对这块进行了微调兼容XA语法,每次XA Start和XA COMMIT时,都会携带GTS值,把GTS写到redo的同时也写到tlog中。为了避免tlog刷新带来频繁的随机读写问题,TDSQL采用WAL机制,tlog文件以内存映射方式提供读写能力。
如图所示,左侧是tlog的buffer,其中tlog buffer和tlogdatafile对应。每次读GTS时,其实是从tlog buffer页面去读。写GTS时也是写tlog buffer同时写入Redo,这样可以保证tlog的持久化。同样当实例crash后,可以在启动阶段构读取到tlog buffer,整个tlog也能构造出一致性。

第四是针对PREPARE的优化。以下图中的场景为例,在T1启动后,当前MySQL节点已经提交的最大GTS为99,所以PREPARE的GTS一定大于99,至少为100,所以此时为PREPARE的记录行绑定了100的GTS。
同时又有活跃事务T2进行了COMMIT操作,其COMMIT的GTS为150和200,所以T2提交后把SET1、SET2两个节点局部缓存的MAX_COMMIT_GTS推进到了150和200。
同时存在的活跃事务T3为一个只读事务,其GTS为250,那么它读取到T1的PREPARE的记录行时事务将会被阻塞,但是如果T3启动时绑定的GTS为99,那么它将直接跳过PREPARE记录,因为通过PREPARE绑定的100 GTS就可以确定,即使将来该记录提交也一定对T3事务不可见。
综上所述,TDSQL针对该流程进行了优化,在进行查询时,如果当前读事务绑定的GTS值比PREPARE的GTS值要小,这时我们不用等PREPARE完成,可以直接去找其undo前镜像,因为该记录“将来的”COMMIT_GTS一定比当前PREPARE的GTS要大。

第五是针对XA的优化。以下图为例,有SET1和SET2,SET1进行单机事务插入,SET2也在进行单机事务插入,即SET1和SET2插入的事务并没有任何因果关系。如果在同一SET上同时发起三笔单机事务,需要为这三次COMMIT请求三次MC获取全局GTS吗?
TDSQL采用了一个优化措施,此时没有请求三次MC获取COMMIT_GTS,而是复用当前MySQL节点上最大的“快照GTS”。如下图第三阶段所示,三个先后开启的非分布式事务,“同时”提交,此时不再请求MC而是本地获取全局最大的快照GTS 253。SET1的GTS与SET2本身没有依赖关系,对SET1而言,发生的是自身的一阶段的分布式事务,所以这里无需通过MC获取全局最新GTS只需要保证事务的局部时序性,这样可以减少和MC之间的通信的开销。

TDSQL在MC开启之后进行多种优化来提升性能。我们在内外部进行多次测试、验证,得出的结论是MC开启之后有一定损耗,但可以有效的控制在10%左右,对业务来说影响比较小。在一些INSERT和UPDATE场景下我们对相关模型也进行了优化调整,所以带来的性能提升非常高。
图中可以看到,我们在一些混合的TPMC场景测试下,在一台16C 64G的机器下测试模型,开启和关闭GTS对性能影响较小。TDSQL使用全局MVCC,再加上轻量的MC特性,可以将GTS的通信次数降低,从而带来较大的收益。

分布式事务或分布式一致性,不仅仅是数据库要去解决的问题,很多场景下应用下也会面临相同的挑战。对于热点账户的入账或者面对面收账等高并发的场景,数据库仍然存在自身极限,我们认为需要结合应用数据库一起做优化。
常规方案是将账户系统进行切割,包括做单元化、大小账户分离等。但是在这些场景中,一个比较极端的场景是:热点账户,只有一行记录,怎么进行拆分?这种场景下,我们就要结合应用优化配合TDSQL来实现。
以下图为例,有两个账户,账户C和账户B之间进行转账,这个过程中一个减少、一个增加。如果我们通过数据库的分布式事务来进行实现,就会有上限值。如果要谋求更低的处理时效,就需采用异步消息的方式。

比如热点商户账户收单等场景下,仅靠数据库层分布式事务已经不能满足海量的并发场景,采用异步消息优化的同时我们也会采用多级对账机制。应用层会有一个交易订单,交易订单标记着这笔分布式事务成功与否。当交易订单形成后,即使该事务在过程中出错,也可以通过应用层的补偿机制对这个事务进行处理。从而实现实时出账+异步批量入账,减少行锁竞争。
在下图的红包转账场景下,还需要分布式缓存+全局缓存来进一步深入优化。这种场景下,除了采用异步账户外,我们还需要将一个热点账户拆成多个平行子账户,使用消息队列和分布式缓存,通过多级缓存机制,分别实现多级对账、多级缓存、平行热点账户、单元化等实现实时出账、批量异步入账。这样可以将转账逻辑从频繁的网络之间的交互变成一次批量请求,也极大提高了系统的并发能力和事务处理的响应速度。
总的来说,在上述这些极限的高并发场景下,我们需要在应用侧和数据库侧做优化和结合,包括采用异步消息、多级缓存机制等。

作为领先的国产分布式数据库,TDSQL针对新型企业级信息化以及快速实现国产化的转型升级需求,具备数据强一致、金融级高可用、高性能低成本、线性水平扩展、企业级安全、便捷智能运维等核心特性。
目前,TDSQL已服务金融、政务、工业制造等多个行业领域。未来五年,TDSQL将帮助1000家金融机构实现核心系统国产化转型,持续助力国产数据库未来发展。
我有一个涉及多台机器、消息队列和事务的问题。因此,例如用户点击网页,点击将消息发送到另一台机器,该机器将付款添加到用户的帐户。每秒可能有数千次点击。事务的所有方面都应该是容错的。我以前从未遇到过这样的事情,但一些阅读表明这是一个众所周知的问题。所以我的问题。我假设安全的方法是使用两阶段提交,但协议(protocol)是阻塞的,所以我不会获得所需的性能,我是否正确?我通常写Ruby,但似乎Redis之类的数据库和Rescue、RabbitMQ等消息队列系统对我的帮助不大——即使我实现某种两阶段提交,如果Redis崩溃,数据也会丢失,因为它本质上只是内存。所有这些让我开始关注erlang和
?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------
我正在尝试在Rails上安装ruby,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio
因此,当我遵循MichaelHartl的RubyonRails教程时,我注意到在用户表中,我们为:email属性添加了一个唯一索引,以提高find的效率方法,因此它不会逐行搜索。到目前为止,我们一直在根据情况使用find_by_email和find_by_id进行搜索。然而,我们从未为:id属性设置索引。:id是否自动索引,因为它在默认情况下是唯一的并且本质上是顺序的?或者情况并非如此,我应该为:id搜索添加索引吗? 最佳答案 大多数数据库(包括sqlite,这是RoR中的默认数据库)会自动索引主键,对于RailsMigration
我已经找到了几个使用datamapper的示例,并且能够让它们正常工作。不过,所有这些示例都是针对sqlite数据库的。我正在尝试将数据映射器与postgresql一起使用。我将datamapper中的调用从sqlite3更改为postgres,并且我已经安装了dm-postgres-adapter。但它仍然不起作用。我还需要做什么? 最佳答案 与SQLite不同,PostgreSQL不将数据库存储在单个文件中。在你拥有createdyourdatabase之后,尝试这样的事情:DataMapper.setup:default,{:
我使用的是遗留数据库,所以我无法控制数据模型。他们使用了很多多态链接/连接表,就像这样createtableperson(per_ident,name,...)createtableperson_links(per_ident,obj_name,obj_r_ident)createtablereport(rep_ident,name,...)其中obj_name是表名,obj_r_ident是标识符。因此链接的报告将按如下方式插入:insertintoperson(1,...)insertintoreport(1,...)insertintoreport(2,...)insertint
我正在创建一个新的Rails3.1应用程序。我希望这个新应用程序重用现有数据库(由以前的Rails2应用程序创建)。我创建了新的应用程序定义模型,它重用了数据库中的一些现有数据。在开发和测试阶段,一切正常,因为它在干净的表数据库上运行,但是当尝试部署到生产环境时,我收到如下消息:PGError:ERROR:column"email"ofrelation"users"alreadyexists***[err::localhost]:ALTERTABLE"users"ADDCOLUMN"email"charactervarying(255)DEFAULT''NOTNULL但是我在迁移中有这
例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果