GVKun编程网logo

Spirit:继承 gh-ost 灵魂的 MySQL 在线大表变更方案

1

在这篇文章中,我们将带领您了解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 在线大表变更方案

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()应该解析什么?

boost-spirit-qi – qi :: uint_parser()应该解析什么?

从测试中我可以得到qi :: uint_parser< int>()与qi :: uint_相同.它们解析从0到UINT_MAX的整数.

我不明白的是qi :: uint_parser要求std :: numeric_limits< T> :: max()对数字基类型T有效.我不确定我是否应该假设qi :: uint_parser< int&gt ;()应该解析从0到std :: numeric_limits< int> :: max()的整数而不是std :: numeric_limits< unsigned int> :: max().或者这个要求与解析器的范围无关?

解决方法

我认为这复制了 How to write a boost::spirit::qi parser to parse an integer range from 0 to std::numeric_limits<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语法中

c – boost :: optional to bool,在boost :: spirit :: qi语法中

在我的boost :: spirit语法中,我有以下片段;
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.

我将如何实现这一目标?

解决方法

我想你正在寻找qi :: matches []:
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.如何将内联解析器表达式转换为独立语法,以及如何解压缩它们生成的元组?

c – Boost :: Spirit :: Qi.如何将内联解析器表达式转换为独立语法,以及如何解压缩它们生成的元组?

我正在使用QI和Phoenix,而且我想写一个小的语法,返回4个bools,这些bool被用作一个语义动作中的函数调用的参数.

我有几个功能需要这些东西,到目前为止我已经使用了这种方法:

( 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,占位符.

这使我看起来好像这个版本还返回一个元组,并且与上面的方法有些不兼容,不像返回一个的语法.

我该如何处理?

解决方法

如果您不发布完整的,连贯的重复,那么诊断您的问题几乎是不可能的;它可能是一个语法错误,它可能是一个缺少#include,谁知道..?

这是一个工作示范希望您可以使用它作为参考,以弄清楚您的代码有什么问题:

///// 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使用自定义跳过语法重新建立跳过

c – Boost Spirit Qi使用自定义跳过语法重新建立跳过

到目前为止,我的语法一直使用标准的boost :: spirit :: ascii :: space / boost :: spirit :: ascii :: space_type skipper.

我有一些使用船长的规则和一些不使用船长的规则

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基类是有原因的,因为我的二进制文件几乎立即就被发现了.

我应该如何使用它?

谢谢

解决方法

qi :: grammar只是qi :: rules的容器.它没有复制构造函数,因为这可能会无意中在这些规则右侧的解析器表达式中创建悬空引用.

像你想要的那样使用语法作为队长是有点棘手的,相当于将语法的开始规则传递给跳过解析器.为此创建规则实例可能更容易(特别是如果您有一个规则管理器).

在任何情况下,规则都需要作为参考传递给船长(通过调用规则的成员函数别名()):

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使用自定义跳过语法重新建立跳过的相关信息,请在本站寻找。

本文标签: