05年腾讯充值和财付通起步开始,腾讯就已经将MySQL/MariaDB全面应用在金融交易类系统中。在近13年支付、充值等海量业务的打磨下,腾讯云数据库团队修复大量MySQL/MariaDB的bug,并优化回馈社区,向MySQL/MariaDB社区贡献了众多代码补丁(Patch )。腾讯云数据库团队与金融云团队共同维护了金融级分布式架构TDSQL,目前,腾讯 90% 的金融、计费、交易类业务核心系统承载在 TDSQL 中。目前TDSQL 可以提供公有云、专有云两种部署方案,可以分配基于MySQL协议的CDB(关系型数据库)实例、DCDB(分布式数据库)实例、ADB(分析型数据库)实例。
我们选择了一部分针对金融交易类业务场景的优化,看看金融交易类业务特点。
金融级高可用和数据强一致
高可用和数据强一致,是金融行业最基本的需求。因此,通常会配置主从架构和强同步复制来保证高可用和强一致,为了防止从机复制执行过慢,一般选择使用row格式的binlog复制。而金融业务系统大量的批处理,会导致一次性更新/删除了很多行数据,此时复制到从机时,则需要使用索引扫描或者全表扫描来找到这个行,可能导致复制效率变得非常慢,这将导致严重的从机数据延迟,进而产生更多问题。
例如,创建一张没有主键的表,假设现在’test’表有100万条记录,在主机上执行一个批处理DELETE FROM test,假如删除了10万行,则会记录10万条binlog记录,如:
delete from test where id=1;
delete from test where id=2;
…
delete from test where id=100000;
binlog被同步到备机,会执行10万个单行删除事件。对于具体每一个单行删除事件,、因为目标表没有主键,需要采用全表扫描,每次执行约100万行,那么共需要扫描10万 × 100万= 1000亿条记录。这可能直接导致从机一直卡住好几天。
为了避免这些问题,我们支持“自动增加主键”。
自动增加主键,该功能主要是在用户创建表(且未定义主键时),自动在表中添加一列自增列作为主键。为此,我们新增了reject_table_no_pk这个参数,该参数设置为 1 时,如果在建表语句中,显示指定主键,系统将自动为该表创建一列自增行作为主键。
而为避免“自动增加主键”功能关闭时(即reject_table_no_pk=0)用户创建表,如果create table语句没有创建主键和唯一索引的子句,那么系统将返回建表错误,错误码为“1173”,提示信息为“This table type requires a primary key”。如果alter table语句删除了主键或者唯一索引,导致这个表没有了主键和唯一索引,也会返回这个错误。
分布式事务大量应用于真实金融级场景
金融交易类系统,会使用大量事务(例如银行转账),且随着数据量逐渐变大,某些系统会选择采用分布式数据库作为核心交易系统,这时又需要支持分布式事务。 腾讯云TDSQL不仅优化了事务处理效率,而且在修复MySQL 5.7 分布式事务的大量Bug,还为社区贡献众多代码补丁(Patch );并且完整实现了MySQL-5.7在分布式事务处理在binlog复制相关功能中的事务灾难恢复处理功能。填补了MySQL在分布式事务处理方面的众多重大缺陷和空白。目前,TDSQL是有限几个在将分布式事务大量应用在真实金融交易场景中的数据库。
例如,社区版本在某些情况下,从机IO线程获取xa事务(分布式事务)的事件期间中断连接后,如果当前relay log上面最后一个事务正好是不完整的XA事务,那么sql线程会卡住。另外官方版本中,没有处理XA 事务分支的二进制日志与存储引擎(binlog + engine) 灾难恢复,而TDSQL不仅处理了XA PREPARE事务分支的灾难恢复,还包括 XA COMMIT … ONE PHASE以及XA COMMIT/XA ROLLBACK 语句的灾难恢复。还考虑到金融高可用场景下,如果事务未执行完成,主机故障从机上面可能无法继续执行或反复切换等Bug。
金融级安全优化,系统安全机制更加健全
安全性是金融行业非常重要的需求,而安全管理制度和安全设备是有可能存在不完善的,为确保不会被非法用户利用,腾讯云做了这几点优化能力:
l 根据大量的入侵案例和报告中,非法用户经常通过select into out file来把其本地的攻击程序传输到服务器端,或采用目录穿越攻击(Path Traversal攻击)随意地打开、查看,甚至执行网站内的文件。腾讯云TDSQL对于tcp/ip登录的用户,即使该账号有权限,也禁止用户通过以下SQL语句在mysqld运行的服务器上面创建或者打开文件select … into dump file; select … into outfile; load data infile … ;并禁止写入”路径变量不是正常路径,而是由字符串组成的 “真值”,或“变量”” 这样的语法来读写路径变量的值。有了这些禁止措施,用户将无法通过TCP/IP连接的客户端在mysqld服务器上面创建或打开任意文件,用户创建的文件只有数据表文件等完全受mysqld掌控的文件。用户只能传输DDL/DML等SQL语句字符串以及得到它们的查询结果。
l 在另外某些案例中,有一些非法用户通过可能通过一些未知的业务系统漏洞去修改系统表,进而提权、做破坏或放开更多漏洞,禁止这种方式后可有效避免类似情况发生;也有用户误删除了系统数据库或表,然后试图挽回,于是自己重新创建了一个系统表,可想而知,其他地方copy过来的系统表,与原系统表会有差异,结果导致各种系统异常。只允许 unix socket 连接且具有权限的用户删除系统数据库 mysql, information/performance_schema, xa, sys, sysdb及其中的表,并且禁止非 unix socket 连接或不具有权限的用户 对mysql.user表进行Drop/alter/truncate 表,或插入、更新、删除这些表中的行。
l MySQL提供了标准的接口允许用户实现自定义的功能(安装插件),这个机制虽然灵活,但也是巨大的安全隐患。如果被非法用户利用,就可以在mysqld服务器上面以linux用户的身份执行任意代码。所以,我们禁止通过TCP/IP连接上来的用户执行INSTALL PLUGIN语句安装插件。
l 我们在MySQL5.7版本以后,全面支持表空间加密和连接加密,加密的主要目的是方式主要对硬盘上的数据库件的透明加密,如果该数据文件被盗取(盗取磁盘、机器),在没有密钥的前提下无法恢复出数据内容。当然,TDSQL在分布式数据库DCDB方案基础上,也实现了加密
l 为在灵活性与安全性中间予以平衡,上述安全机制都有开关控制(默认打开),为避免非法用户关闭以越过安全机制,我们禁止远程用户修改和读取这些开关变量,防止非法用户关闭上述安全措施。
生产系统非标操作限制,降低系统负载
在某些情况下,开发或运维不得不直连数据库做某些操作。而某个不合理的SQL语句会直接导致数据库开销过大,会严重增加系统负载,降低系统的TPS,甚至影响系统可用性。有时候,某些SQL开销过大并不是用户需求,可能只是人为错误,甚至SQL注入等。因此,腾讯在数据库内核中可以限制SQL语句执行的规模。即通过限制临时表的大小,或强制delete/update/select增加limit子句。
对于创建在myisam上面的临时表,用户可以设置全局变量 max_temp_table_size来限定临时表的大小,该值默认是0,表示没有限制。 例如,可以设置max_temp_table_size=16K,当查询执行过程中任意一个临时表的temp_table_size大于这个限定值时候,查询直接返回失败,服务器端错误号:1114,错误字符串是:“The table ‘xxx’ is full。”
用户可以设置select_rows_limit/delete_rows_limit/update_rows_limit 3个动态的全局变量,来强制添加用户必须显示指定limit子句来做规模限制,例如设置比如 set global delete_rows_limit=10000; 这样的话,delete语句就不可以删除超过10000行代码,否则就失败了。当然,开启变量后,用户也可以显示指定NO_LIMIT关键字,来避免这个限制。
为了防止计划外的超大事务导致锁表;或binlog量过大会导致强同步等待超时;或某些异常操作,比如用户误操作无条件删表等;我们通过参数max_binlog_write_threshold限制一个事务中写入的binlog总量,当达到配置的最大值时候,当前DML语句返回错误,并在数据库中回滚该事务。检查在DML执行时即做,而不是在事务提交时刻,这样可以有效提高效率。
除了上述之外,腾讯云TDSQL还有大量其他优化,由于篇幅有限,我们会在未来逐步在腾讯云社区中公开;另外,我们已将大量优化和改动都提交社区开源。
作者:赵伟,腾讯云资深工程师,数据库内核开发者,曾就职于Oracle公司,已从事数据库系统开发10余年,为MySQL社区提供了众多优秀特性。