在这篇文章中,我们将带领您了解Spirit:继承gh-ost灵魂的MySQL在线大表变更方案的全貌,同时,我们还将为您介绍有关boost-spirit-qi–qi::uint_parser()应该解析
在这篇文章中,我们将带领您了解Spirit:继承 gh-ost 灵魂的 MySQL 在线大表变更方案的全貌,同时,我们还将为您介绍有关boost-spirit-qi – qi :: uint_parser()应该解析什么?、c – boost :: optional to bool,在boost :: spirit :: qi语法中、c – Boost :: Spirit :: Qi.如何将内联解析器表达式转换为独立语法,以及如何解压缩它们生成的元组?、c – Boost Spirit Qi使用自定义跳过语法重新建立跳过的知识,以帮助您更好地理解这个主题。
本文目录一览:- Spirit:继承 gh-ost 灵魂的 MySQL 在线大表变更方案
- boost-spirit-qi – qi :: uint_parser()应该解析什么?
- c – boost :: optional to bool,在boost :: spirit :: qi语法中
- c – Boost :: Spirit :: Qi.如何将内联解析器表达式转换为独立语法,以及如何解压缩它们生成的元组?
- c – Boost Spirit Qi使用自定义跳过语法重新建立跳过
Spirit:继承 gh-ost 灵魂的 MySQL 在线大表变更方案
昨天看到社区发布了一个新的 MySQL 大表变更工具 Spirit。是海外支付巨头 Block 旗下的 Cash App (地位类似于支付宝) 开源的,作者之前也在 PingCAP 工作过。
目前市面上做大表变更的方案有两个:
- Percona 开源的 pt-online-schema-change,基于 trigger。
- GitHub 开源的 gh-ost,基于 binary log。
业界更多采用 gh-ost 方案。Bytebase 也集成了基于 gh-ost 的大表变更方案。
Spirit 也是继承了 gh-ost 的灵魂,整体框架沿用 gh-ost。Spirit 这个名字(中文:精神)也是致敬的 Ghost。
Spirit 对于 gh-ost 的改进点:
- 多线程复制
- 断点恢复
- 自适应复制数据的速度
- 通过设置水位标和保存 Change Row Map 避免复制不必要的数据
这其中最有价值的改进点是 2 - 断点恢复。之前 gh-ost 的问题是一旦断开,就要重头再来,变更一张大表,有时要几天,好不容易等到快结束时, gh-ost 挂了,一切从头来过,那大家都奔溃。
而大表变更跑的越久,挂掉的可能性就越高。尤其现在大家都跑在 K8s 上,pod 挂掉本身就是预期的。要知道数据库变更没做完,新的应用版本就没法上线,新的应用版本没法上线,新的运营活动就不能展开。这种延期对于像 Cash app 这样的业务是不可接受的。
Spirit 和 gh-ost 还有两个需要注意的区别:
- Spirit 不像 gh-ost 那样提供激进的限流
- Spirit 只支持 MySQL 8.0
我想这最后一条估计会劝退不少国内的用户,虽然 MySQL 5.7 已经官宣了 EOL,但国内 MySQL 5.6 也不在少数。
代码刚开源在 cashapp/spirit,采用 Apache 证书,知道的人还不多。如果能持续维护下去,有希望替代 gh-ost 成为 MySQL 大表变更的首选方案。
更多资讯,请关注 Bytebase 公号:Bytebase
boost-spirit-qi – qi :: uint_parser()应该解析什么?
我不明白的是qi :: uint_parser要求std :: numeric_limits< T> :: max()对数字基类型T有效.我不确定我是否应该假设qi :: uint_parser< int> ;()应该解析从0到std :: numeric_limits< int> :: max()的整数而不是std :: numeric_limits< unsigned int> :: max().或者这个要求与解析器的范围无关?
解决方法
由于它显然没有记录,你测试过吗?
Or is this requirement has nothing to do with the parser’s range?
我希望它是后者.我希望属性类型说明属性,而不是解析器.解析器解析,属性传播分配给属性.关注点分离.
属性传播可能会进行C语言的任何隐式转换.这实际上与在scanf(“%d”)中使用unsigned或使用std :: istream读取unsigned int没有什么不同:
见How to read unsigned int variables from file correctly,using ifstream?
我希望这一切都是真的,因为C结合了语言的核心价值
>只为您需要的东西付费
>程序员知道他在做什么.
事实上,这是问题的根源:Spirit定义了qi :: int_,qi :: uint_和朋友.如果你使用底层的qi :: [u] int_parser<>来拼凑自定义组合模板,你告诉编译器“我知道我在做什么”.
免责声明:所有这些都是非权威性的.我甚至没有检查代码/文档.我认为这本质上是一个文档问题,在mailing list更好
c – boost :: optional to bool,在boost :: spirit :: qi语法中
implicit_method_declaration = (-(qi::token(ABSTRACT)) >> ...)
– (qi :: token(ABSTRACT)的类型是boost :: optional< boost :: iterator_range< std :: string :: iterator>>但是我只是使用这个构造来检查是否抽象关键字,实际上是现在,也就是说,我宁愿 – (qi :: token(ABSTRACT)的类型为bool,值为boost :: optional< ...> operator bool()const.
我将如何实现这一目标?
解决方法
implicit_method_declaration = qi::matches[qi::token(ABSTRACT)] >> ...;
另一种方法是使用qi :: attr()和替代方法:
implicit_method_declaration = ( qi::token(ABSTRACT) >> qi::attr(true) | qi::attr(false) ) >> ...;
再次快速演示:http://coliru.stacked-crooked.com/a/ed8bbad53e8c1943
#include <boost/spirit/include/qi.hpp> namespace qi = boost::spirit::qi; template <typename It,typename Skipper = qi::space_type> struct parser : qi::grammar<It,bool(),Skipper> { parser() : parser::base_type(implicit_method_declaration) { using namespace qi; implicit_method_declaration = matches["abstract"]; BOOST_SPIRIT_DEBUG_NODES((implicit_method_declaration)); } private: qi::rule<It,Skipper> implicit_method_declaration; }; bool doParse(const std::string& input) { typedef std::string::const_iterator It; auto f(begin(input)),l(end(input)); parser<It,qi::space_type> p; bool data; try { bool ok = qi::phrase_parse(f,l,p,qi::space,data); if (ok) { std::cout << "parse success\n"; std::cout << "data: " << data << "\n"; } else std::cerr << "parse Failed: '" << std::string(f,l) << "'\n"; if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n"; return ok; } catch(const qi::expectation_failure<It>& e) { std::string frag(e.first,e.last); std::cerr << e.what() << "'" << frag << "'\n"; } return false; } int main() { doParse("abstract"); doParse("static final"); }
产量
parse success data: 1 parse success data: 0 trailing unparsed: 'static final'
c – Boost :: Spirit :: Qi.如何将内联解析器表达式转换为独立语法,以及如何解压缩它们生成的元组?
我有几个功能需要这些东西,到目前为止我已经使用了这种方法:
( qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool)[px::bind(&Bool4Function,spirit::_val,spirit::_1,spirit::_2,spirit::_3,spirit::_4)]
虽然它是自己的,但是使用它就是简单的丑陋和混乱,即使使用命名空间的部分.
这就是为什么我想把这个表达式提取成一个独立的语法.
所以我试过这个(信用到ildjarn为测试台):
///// grammar implementation /////#include <boost/fusion/include/vector10.hpp>#include <boost/spirit/include/qi_bool.hpp>#include <boost/spirit/include/qi_char_.hpp>#include <boost/spirit/include/qi_grammar.hpp>#include <boost/spirit/include/qi_operator.hpp>#include <boost/spirit/include/qi_rule.hpp>#include <boost/spirit/include/qi_string.hpp>struct FourBools : boost::spirit::qi::grammar< char const*,boost::fusion::vector4<bool,bool,bool>()>{ typedef boost::fusion::vector4<bool,bool> attribute_type; FourBools() : base_type(start_) { using boost::spirit::bool_; start_ = "4bools:" >> bool_ >> ',' >> bool_ >> ',' >> bool_ >> ';' ; }private: boost::spirit::qi::rule< base_type::iterator_type,base_type::sig_type > start_;};FourBools const fourBools;///// demonstration of use /////#include <string>#include <ios>#include <iostream>#include <boost/fusion/include/at_c.hpp>#include <boost/spirit/include/phoenix_bind.hpp>#include <boost/spirit/include/phoenix_core.hpp>#include <boost/spirit/include/qi_action.hpp>#include <boost/spirit/include/qi_parse.hpp>void noDice(bool a,bool b,bool c,bool d) {}void worksFine(boost::fusion::vector4<bool,bool> a){}int main(){ namespace phx = boost::phoenix; namespace spirit = boost::spirit; std::string const input("4bools:true,true,false;"); char const* first = input.c_str(); char const* const last = first + input.size(); bool const success = spirit::qi::parse( first,last,fourBools[phx::bind(&noDice,spirit::_1)] ); if (!success) std::cout << "parse() Failed\n"; else if (first != last) std::cout << "didn't consume all input\n"; std::cout.flush();}
除非使用fourBools [phx :: bind(& worksFine,spirit :: _ 1)]替换fourBools [phx :: bind(& noDice,spirit :: _ 1)],否则不会编译.
这意味着我的问题是解开匹配要调用的函数的签名的参数,因为参数的数量在签名级别是不同的(一个是四个bool的元组,也可以是四个bool).
是否可以直接使用凤凰占位符进行打包,而不是写入包装器,将元组转换为需要单独的现有功能的单独参数?
如果是,那么语法是什么呢?
毕竟,一个内联版本(qi :: _ bool>> qi :: _ bool>> qi :: _ bool>> qi :: _ bool)在精神上“解压缩”时工作正常:: _ 1 – 精神:: _ 4,占位符.
这使我看起来好像这个版本还返回一个元组,并且与上面的方法有些不兼容,不像返回一个的语法.
我该如何处理?
解决方法
这是一个工作示范希望您可以使用它作为参考,以弄清楚您的代码有什么问题:
///// grammar implementation /////#include <boost/fusion/include/vector10.hpp>#include <boost/spirit/include/qi_bool.hpp>#include <boost/spirit/include/qi_char_.hpp>#include <boost/spirit/include/qi_grammar.hpp>#include <boost/spirit/include/qi_operator.hpp>#include <boost/spirit/include/qi_rule.hpp>#include <boost/spirit/include/qi_string.hpp>struct FourBools : boost::spirit::qi::grammar< char const*,bool> attribute_type; FourBools() : base_type(start_) { using boost::spirit::bool_; start_ = "4bools:" >> bool_ >> ',' >> bool_ >> ',' >> bool_ >> ';' ; }private: boost::spirit::qi::rule< base_type::iterator_type,base_type::sig_type > start_;};FourBools const fourBools;///// demonstration of use /////#include <string>#include <ios>#include <iostream>#include <boost/fusion/include/at_c.hpp>#include <boost/spirit/include/phoenix_bind.hpp>#include <boost/spirit/include/phoenix_core.hpp>#include <boost/spirit/include/qi_action.hpp>#include <boost/spirit/include/qi_parse.hpp>typedef FourBools::attribute_type attr_t;struct verify_same{ explicit verify_same(attr_t const& expected) : expected_(expected) { } void verify(attr_t const& actual) const { using boost::fusion::at_c; std::cout << std::boolalpha << "same as expected: " << (actual == expected_) << "\nactual values: " << at_c<0>(actual) << ' ' << at_c<1>(actual) << ' ' << at_c<2>(actual) << ' ' << at_c<3>(actual) << '\n'; }private: attr_t expected_;};int main(){ namespace phx = boost::phoenix; namespace spirit = boost::spirit; std::string const input("4bools:true,false;"); verify_same const vs(attr_t(true,false)); char const* first = input.c_str(); char const* const last = first + input.size(); bool const success = spirit::qi::parse( first,fourBools[phx::bind(&verify_same::verify,phx::cref(vs),spirit::_1)] ); if (!success) std::cout << "parse() Failed\n"; else if (first != last) std::cout << "didn't consume all input\n"; std::cout.flush();}
除此之外,我认为使用纯均匀类型的元组是奇怪的;个人来说,我会将语法的合成属性更改为boost :: array< bool,4>.
编辑(响应OP的编辑):有好消息和坏消息,更好的消息.
这是一个好消息:Boost.Fusion具有使用最少代码完成所需功能的功能:boost::fusion::fused<>
.这将需要一个可调用类型(包括自由功能指针和成员函数指针),它可以接受多个参数并将该可调用类型包装一个融合序列的函子;当调用该函子时,它需要Fusion序列并解包它,将元组的各个元素转发到包装的可调用类型作为单独的参数.
所以,考虑到我已经发布的语法和以下内容:
#include <string>#include <ios>#include <iostream>#include <boost/fusion/include/at_c.hpp>#include <boost/fusion/include/make_fused.hpp>#include <boost/spirit/include/phoenix_bind.hpp>#include <boost/spirit/include/phoenix_core.hpp>#include <boost/spirit/include/phoenix_fusion.hpp>#include <boost/spirit/include/qi_action.hpp>#include <boost/spirit/include/qi_parse.hpp>typedef FourBools::attribute_type attr_t;void free_func_taking_tuple(attr_t const& tup){ using boost::fusion::at_c; std::cout << std::boolalpha << "inside free_func_taking_tuple() :: " << at_c<0>(tup) << ' ' << at_c<1>(tup) << ' ' << at_c<2>(tup) << ' ' << at_c<3>(tup) << '\n';}void free_func_taking_bools( bool const a,bool const b,bool const c,bool const d){ std::cout << std::boolalpha << "inside free_func_taking_bools() :: " << a << ' ' << b << ' ' << c << ' ' << d << '\n';}
boost :: spirit :: qi :: parse()可以这样调用:
namespace phx = boost::phoenix;namespace spirit = boost::spirit;using boost::fusion::make_fused;// calls free_func_taking_tuple,nothing new herespirit::qi::parse( first,fourBools[phx::bind(free_func_taking_tuple,spirit::_1)]);// calls free_func_taking_bools,using boost::fusion::fused<> to unpack the tuple// into separate argumentsspirit::qi::parse( first,fourBools[phx::bind(make_fused(&free_func_taking_bools),spirit::_1)]);
这是一个坏消息:Boost.Fusion的可调用类型包装器依赖于TR1 / C 11 result_of协议,而Boost.Phoenix v2实现了Boost.Lambda result_of协议 – 这些不兼容.因此,您必须自己解压元组元素:
namespace phx = boost::phoenix;namespace spirit = boost::spirit;spirit::qi::parse( first,fourBools[phx::bind( free_func_taking_bools,phx::at_c<0>(spirit::_1),phx::at_c<1>(spirit::_1),phx::at_c<2>(spirit::_1),phx::at_c<3>(spirit::_1) )]);
呸!但是,还有一个好消息:Boost.Phoenix v3将在Boost 1.47中发布,它实现了TR1 / C11的result_of协议.因此,从Boost 1.47开始,您将可以使用boost :: fusion :: fused&并节省自己一些繁琐的样板.
c – Boost Spirit Qi使用自定义跳过语法重新建立跳过
我有一些使用船长的规则和一些不使用船长的规则
qi::rule<Iterator,PTR<Expression>(),ascii::space_type> expression; qi::rule<Iterator,PTR<Term>()> term;
当我在跳过非终结符(如表达式)中使用非跳过非终结符(如术语)时,一切都像我期望的那样 – 空白只在非终结词内部很重要.
此外,到目前为止,我一直很好,包括使用不使用qi :: skip重新跳过的非终结者内部的队长的非终结者,例如
index = (qi::lit('[') >> qi::skip(ascii::space)[explist >> qi::lit(']')]);
这样,[]括号内的空格不重要,但在外面.
但是,现在我想添加自己的自定义队列(我想让换行符显着,然后添加注释跳过).我的船长语法看起来像:
struct skip_grammar : qi::grammar<Iterator> { qi::rule<Iterator> start; skip_grammar() : skip_grammar::base_type(start) { start = qi::char_("\t\r "); } };
我已经能够将它添加到我的规则定义中就好了
qi::rule<Iterator,skip_grammar> expression;
但我似乎无法弄清楚如何使用我的跳过语法作为qi :: skip(并替换ascii :: space)的参数.我尝试使用类型,本地实例变量和全局实例变量.我得到的最远的是让clang抱怨我的skip_grammar需要一个拷贝构造函数.所以我尝试在我的跳过语法中添加一个复制构造函数,但显然boost :: noncopyable基类是有原因的,因为我的二进制文件几乎立即就被发现了.
我应该如何使用它?
谢谢
解决方法
像你想要的那样使用语法作为队长是有点棘手的,相当于将语法的开始规则传递给跳过解析器.为此创建规则实例可能更容易(特别是如果您有一个规则管理器).
在任何情况下,规则都需要作为参考传递给船长(通过调用规则的成员函数别名()):
skip_grammar skippper; index = '[' >> qi::skip(skipper.start.alias())[explist >> ']'];
或者干脆:
rule<iterator> skipper = qi::char_("\t\r "); index = '[' >> qi::skip(skipper.alias())[explist >> ']'];
别名是必要的,因为复制规则意味着什么.在Spirit的FAQ here中有更详细的描述.
关于Spirit:继承 gh-ost 灵魂的 MySQL 在线大表变更方案的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于boost-spirit-qi – qi :: uint_parser()应该解析什么?、c – boost :: optional to bool,在boost :: spirit :: qi语法中、c – Boost :: Spirit :: Qi.如何将内联解析器表达式转换为独立语法,以及如何解压缩它们生成的元组?、c – Boost Spirit Qi使用自定义跳过语法重新建立跳过的相关信息,请在本站寻找。
本文标签: