admin

  • 过去的2023年

    ·

    我们一家人一起去吃了很多次海底捞,还在那里过了一次生日,哥哥如他说所,尴尬得躲到了桌子底下。不过,吃得依旧非常的欢乐。

    人生第一次去了西安,这个中国古都。是在咸阳机场落地,然后在雁塔区做了一场关于云数据库架构与选型的分享。之后,去秦岭脚下的西北工大,见到了十几年未见的研究生同学,他的生活还是和在学校时差不多,他们学校的烤鱼味道真的不错。

    虽然是江西人,但是这是我们第一次去滕王阁。还有八一纪念馆没有去过、江西省博物馆也没有去过,打算春节期间再去这些地方看看

    一家人一起去了黄山,虽然爬山有点累,但是都很开心。看到了传说中的迎客松、猴子观海、天都峰,黄山的松、石确实奇美,古人诚不欺我:参考

    今年后院的柚子树,依旧结了很多柚子。不过,柚子树有一根非常重要支干被虫子蛀得非常厉害,老爸虽然已经把这部分清理掉了,不知道明年柚子树是否还能够保持健康。今年结的柚子,水份很足,味道很好。

    这一年,经常会在梦中见到妈,妈变得话很少,总是会在远远的地方看着我。每次醒来,都很想回忆起梦中的更多细节,不过,似乎越想去记得清晰,梦中的场景却会变得越模糊。

    爸妈年轻时候的样子

    今年,休假了一次大长假,全家一起去了一趟大西北。看到了似湖似海的青海湖,翻过了葱郁的祁连山,领略了发着金光的岗什卡雪山,经过了杳无人烟的火星公路,见证了石壁上环绕千年敦煌飞天。

    终于玩上了《塞尔达传说:王国之泪》:做好了一刀入魂的斩马刀;帮助渔民赶走了海盗,重建了渔村;解决了哈特诺村传统与时尚的矛盾;运送了无数的呀哈哈;帮助搭建了无数的松达家的广告牌;从人马竞技场进货无数…… 唯独,还没有去救公主

    一起去了人很多很多很多的黄鹤楼,人真的很多。不过,除了黄鹤楼,武汉还有湖北省博物馆、辛亥革命纪念馆、热干面,都不错

    今年,还斥巨资购入了新的相机,佳能R8+RF24-70/F2.8。给拍了很多照片,有家人们、有去过的景点、也有日常生活的记录。右边这张照片,曾被西湖文旅的公众号选中,作为图书馆的宣传照片。哦,今年还去了十次图书馆,希望明年能够去得更多。

    元旦,我们还去了上海。去看了四姨家的小宝宝,去了上海天文馆、航海博物馆。

    公司的业务,有顺利的一面,也有很有挑战的一面。整体的环境比较困难,内力与招式需要双修,才能适应复杂的外部环境。

    今年做了第一次云数据库性能测试,算是给自己挖了一个大坑,希望后续能够持续关注云数据库发展,持续的进行性能测试。

    多年的老同事从澳洲回杭州,借机会也把附近的原淘宝DBA的同事聚了起来,多年不见,再次相聚,非常开心

  • 在MySQL社区的帮助下,终于成功开通了Oracle Cloud,于是,第一时间测试了一下在Oracle Cloud上托管MySQL,看看Oracle MySQL原厂的性能表现如何。

    关注我朋友圈的应该知道,在国内自助的注册Oracle Cloud真的非常不容易。所以,特别感谢Oracle MySQL原厂工程师徐轶韬的帮助,他也是《MySQL高可用解决方案-从主从复制到InnoDB Cluster架构》的作者,他的公众号是“MySQL解决方案工程师”,感兴趣可以关注他的公众号。

    回到正文,Oracle Cloud的全称是”Oracle Cloud Infrastructure”,也经常被简称为”OCI”。在OCI上提供的MySQL服务,相较于AWS、阿里云来说,产品形态也比较简单。首先,托管MySQL在分类“Databases->MySQL HeatWave->DB Systems”这个类目下,在实例创建过程中,涉及的选项不算多,但是因为名词/定义与其他云厂商有一些不同,所以这里做一些解释。Oracle Cloud上的托管MySQL主要选项为:

    • 高可用类型(单节点/standalone、三节点/HA)
    • 规格大小
    • 主可用区选择
    • 空间大小(IOPS/吞吐量与此相关)

    在本次测试中,一共进行了三组测试。规格大小统一为:2 OCPU(Oracle CPU)32GB,100GB存储(对应的7500 IOPS),规格代码为MySQL.VM.Standard.E4.2.32GB。三组测试对比项分别为:单节点 vs 三节点和同可用区 vs 跨可用区。三组测试具体为:跨可用区的三节点测试、同可用区的三节点测试、同可用区的单节点测试。详细参数表如下:

    整体性能趋势图

    整体上,OCI上的MySQL高可用版本(MGR三节点)性能要比单节点低约20%。在三节点的两次测试下,跨可用区与同可用区有着几乎相同的性能表现,从延迟上来看,也非常接近,可用区之间的延迟约为百微秒级别。

    相比于其他云厂商,OCI上的MySQL性能表现,并不算高的。从架构层面,Oracle MySQL是唯一一个选择了MGR来实现高可用、跨可用区数据一致性的。

    OCI上的区域与可用区

    在MySQL的实例创建过程中,会有一些诸如:AD、FD(Fault Domins)等选项。这是OCI特有的关于区域、可用区等选项。关于区域和可用区的概念,Oracle Cloud与其他云厂商虽然都是相同的概念,但是用了完全不同的名称,也是非常容易让人困惑的。

    在Oracle中,分了几层概念:Region->Availability Domains->Fault Domains。

    • Region与其他云厂商区域概念相同;
    • Fault Domains则与其他云厂商的“可用区”对应;
    • 在OCI中,通常三个为一组的Fault Domains会组成一个“Availability Domains”。大部分的Oracle Region中只有一个Availability Domains,也有部分region有3个Availability Domains(参考),所以在MySQL的配置选项中会有AD、FD等缩写词语。

    Oracle Cloud上MySQL实例的数据可靠性架构

    OCI上的MySQL使用的MGR来实现高可用、高可靠(参考:High Availability@Oracle Cloud Infrastructure DocumentationGroup Replication@MySQL Documentation)。三个不同的MySQL节点分布在三个不同的FD,每个节点使用的存储是OCI上的Block Volume(参考),以iSCSI方式使用,类型是“Higher Performance”(此外,还有Balanced、Ultra High Performance、Lower Cost三种Block Volume)。

    根据参数配置来看,Oracle Cloud提供的MySQL三节点是通过MGR实现的,使用的是group_replication_single_primary_mode模式。

    mysql> show variables like '%group_replication_single_primary_mode%';
    +---------------------------------------+-------+
    | Variable_name                         | Value |
    +---------------------------------------+-------+
    | group_replication_single_primary_mode | ON    |
    +---------------------------------------+-------+
    1 row in set (0.00 sec)

    其他说明

    • Oracle Cloud的MySQL没有提供两节点选项
    • 产品规格,Oracle Cloud使用的是OCPUs(Oracle CPU,可以理解为core),详细参考:vCPU and OCPU pricing information。相比于其他云厂商的vCPU(大部分时候为超线程)是明显不同的。

    性能测试的原始数据

    2024-01@MySQL at Oracle Cloud Benchmark
    host : 10.0.0.74 
    sub_dir : 10.0.0.74 
    ins_code : MySQL.VM.Standard.E4.2.32GB 
    ha_type : ha 
    same_az :  
    region : tokyo 
    storage_size : 100 
    
    sysbench for host :10.0.0.74
    threads|transactions| queries| time |avg/Latency|95%/Latency
          2|       90638| 1812760|300.01|       6.62|       7.84 
          4|      147270| 2945400|300.01|       8.15|      10.09 
          8|      215078| 4301560|300.01|      11.16|      13.70 
         16|      242713| 4854260|300.02|      19.78|      33.72 
         24|      260072| 5201440|300.02|      27.68|      45.79 
         32|      273703| 5474060|300.03|      35.07|      56.84 
         48|      283811| 5676220|300.06|      50.74|      73.13 
         64|      287176| 5743520|300.06|      66.86|     102.97 
         96|      283113| 5662260|300.09|     101.74|     158.63 
        128|      285210| 5704200|300.12|     134.66|     189.93 
        192|           0|       0|  0.00|       0.00|       0.00 
    
    2024-01-14@MySQL at Oracle Cloud Benchmark
    host : 10.0.0.13 
    sub_dir : 10.0.0.13 
    ins_code : MySQL.VM.Standard.E4.2.32GB 
    ha_type : ha 
    same_az : 1 
    region : tokyo 
    storage_size : 100 
    
    sysbench for host :10.0.0.13
    threads|transactions| queries| time |avg/Latency|95%/Latency
          2|       95033| 1900660|300.01|       6.31|       7.56 
          4|      148751| 2975020|300.01|       8.07|      10.27 
          8|      214974| 4299480|300.01|      11.16|      13.70 
         16|      262105| 5242100|300.01|      18.31|      30.81 
         24|      253583| 5071660|300.03|      28.39|      47.47 
         32|      272755| 5455100|300.03|      35.20|      54.83 
         48|      269370| 5387400|300.04|      53.46|      77.19 
         64|      286891| 5737820|300.06|      66.93|     102.97 
         96|      276749| 5534980|300.12|     104.08|     161.51 
        128|      287452| 5749040|300.13|     133.61|     183.21 
        192|           0|       0|  0.00|       0.00|       0.00 
    
    host : 10.0.0.74 
    sub_dir : 10.0.0.74 
    ins_code : MySQL.VM.Standard.E4.2.32GB 
    ha_type : ha 
    same_az :  
    region : tokyo 
    storage_size : 100 
    
    sysbench for host :10.0.0.74
    threads|transactions| queries| time |avg/Latency|95%/Latency
          2|       90638| 1812760|300.01|       6.62|       7.84 
          4|      147270| 2945400|300.01|       8.15|      10.09 
          8|      215078| 4301560|300.01|      11.16|      13.70 
         16|      242713| 4854260|300.02|      19.78|      33.72 
         24|      260072| 5201440|300.02|      27.68|      45.79 
         32|      273703| 5474060|300.03|      35.07|      56.84 
         48|      283811| 5676220|300.06|      50.74|      73.13 
         64|      287176| 5743520|300.06|      66.86|     102.97 
         96|      283113| 5662260|300.09|     101.74|     158.63 
        128|      285210| 5704200|300.12|     134.66|     189.93 
        192|           0|       0|  0.00|       0.00|       0.00 
    
    host : 10.0.0.224 
    sub_dir : 10.0.0.224 
    ins_code : MySQL.VM.Standard.E4.2.32GB 
    ha_type : standalone 
    same_az : 1 
    region : tokyo 
    storage_size : 100 
    
    sysbench for host :10.0.0.224
    threads|transactions| queries| time |avg/Latency|95%/Latency
          2|      102033| 2040660|300.00|       5.88|       7.56 
          4|      186602| 3732040|300.01|       6.43|       8.90 
          8|      281464| 5629280|300.01|       8.53|      12.30 
         16|      333418| 6668360|300.02|      14.40|      31.94 
         24|      345463| 6909260|300.02|      20.84|      47.47 
         32|      359902| 7198040|300.03|      26.67|      56.84 
         48|      353246| 7064920|300.06|      40.77|      78.60 
         64|      370118| 7402360|300.07|      51.88|      99.33 
         96|      380647| 7612940|300.09|      75.67|     134.90 
        128|      372683| 7453660|300.12|     103.05|     164.45 
        192|           0|       0|  0.00|       0.00|       0.00 
    

    参考

    1. High Availability@Oracle Cloud Infrastructure Documentation
    2. Group Replication@MySQL Documentation
    3. vCPU and OCPU pricing information

  • 最近,在ACMUG(中国MySQL用户组)的年度活动中,较为详细对之前的云数据库 RDS MySQL性能测试做了分享。如下为分享的PDF,供参考:

    (more…)
  • 随着云数据库的普及,数据库的传输加密也会随之被广泛使用。本文,将使用通用测试来看看,开启TLS传输对数据库的性能有什么样的影响。

    1. 原理概述

    开启TLS传输加密之后,分别会在建立连接、数据传输两个阶段对性能造成影响。因为建立连接的过程通常是一次性的,连接会被复用,所以这部分的性能开销通常是可以接受的。在传输阶段,是传输加密对性能影响的重要阶段,这时候通常会使用对称加密算法(例如AES256)对数据进行加密,那么实际的对称加密的性能就是对TLS传输加密性能影响最大的部分。需要注意的是,如果应用使用的是短链接(应该尽量避免使用这种方式),TLS加密的密钥交换阶段也会对每个连接建立的过程都有一定的性能影响。

    2. 云数据库实际测试

    这里选择对RDS MySQL进行测试,云厂商则各自选择国外、国内Top 1的云厂商AWS和阿里云。

    2.1 测试方法说明

    使用sysbench 1.1.0版本,测试类型为oltp_read_write,相关参数如下:

    • –mysql-ssl=REQUIRED 是否使用TLS加密传输
    • –percentile=95 关注95%Query的延迟
    • –histogram=on 可以查看延迟分布情况
    • –time=600 sysbench运行的总时间(秒)
    • –warmup-time=60 开始计算性能前预热时间(秒)
    • –table_size=1000000 单表100万条记录
    • –tables=5 共测试5个表
    (more…)
  • 这是一个系列文章,旨在了解如何使用Flex/Lex和Yacc/Bison进行词法和语法解析。在前面,已经完成了使用Lex/flex做基础的词法解析实现一个简单的计算器flex/bison系列3:更复杂的一个编译程序实现(上)。在上篇中,已经完成语法规则、主要的数据结构设计。这里就继续完成程序,最后编译测试。

    回顾

    这个系列我们需要通过flex/bison实现一个编译程序,能够实现一种简单的程序语言,这个程序语言包含了:基础运算、变量与赋值、表达式计算、if语句、while语句、print语句等。例如,使用该程序语言,我们可以实现如下程序:

    i = 1;
    a = 0;
    while ( a < 100 ) {
      i = i + 1;
      a = a + i;
    }
    print i;

    该程序解决的问题是:在自然数序列(1、2、4…)中,前面多少个自然数的和首次大于100。你可以使用上面定义的语言,编写自己的程序。

    好了,接着前面三篇的内容,我们继续完成该语言的编译程序。

    主要的函数实现

    build_node函数
    t_node* build_node(enum NODETYPE nt,t_node* left,t_node* right, int i){
        debug_print(__FILE__,__LINE__,__func__,"");
        t_node *t_n;
        t_n = NULL;
        t_n = (t_node *)malloc(sizeof(t_node));
        if (t_n == NULL){
            printf("Out of Memory\n");
            exit(1);
        }
        t_n->nt = nt;
        t_n->left = left;
        t_n->right = right;
        t_n->i = i;
        return t_n;
    }
    exec_node函数
    int exec_node(t_node *n){
        if( n == NULL ) return 0;
        debug_print(__FILE__,__LINE__,__func__,"enter exec_node");
    
        switch(n->nt){
            case NT_INTEGER:
        	    debug_print(__FILE__,__LINE__,__func__,"NT_INTEGER node");
    	    break;
            case NT_VAR_NAME:
        	    debug_print(__FILE__,__LINE__,__func__,"NT_VAR_NAME node");
    	    break;
            case NT_O_ADD:
                exec_node(n->left);
                exec_node(n->right);
                n->i = get_node_ret(n->left) + get_node_ret(n->right);
        	    debug_print(__FILE__,__LINE__,__func__,"NT_O_ADD node");
    	    break;
            case NT_O_MINUS:
                exec_node(n->left);
                exec_node(n->right);
                n->i = get_node_ret(n->left) - get_node_ret(n->right);
        	    debug_print(__FILE__,__LINE__,__func__,"NT_O_MINUS node");
                break;
            case NT_O_MULTIPLY:
        	    debug_print(__FILE__,__LINE__,__func__,"NT_O_MULTIPLY node");
                exec_node(n->left);
                exec_node(n->right);
                n->i = get_node_ret(n->left) * get_node_ret(n->right);
                break;
            case NT_BOOL_EXPR_GT:
        	    debug_print(__FILE__,__LINE__,__func__,"NT_BOOL_EXPR_GT node");
                n->i = 0;
                exec_node(n->left);
                exec_node(n->right);
                if (get_node_ret(n->left) > get_node_ret(n->right) )
                { n->i = 1 ; }
                break;
            case NT_BOOL_EXPR_LT:
        	    debug_print(__FILE__,__LINE__,__func__,"NT_BOOL_EXPR_LT node");
                n->i = 0;
                exec_node(n->left);
                exec_node(n->right);
                if (get_node_ret(n->left) < get_node_ret(n->right) )
                { n->i = 1 ; }
                break;
            case NT_IF:
        	    debug_print(__FILE__,__LINE__,__func__,"NT_IF node");
                exec_node(n->left);
                if (get_node_ret(n->left)){
                    exec_node(n->right);
                }
                break;
            case NT_WHILE:
        	    debug_print(__FILE__,__LINE__,__func__,"NT_WHILE node");
                exec_node(n->left);
                while( get_node_ret(n->left)  ){
                    exec_node(n->right);
                    exec_node(n->left);
                }
                break;
            case NT_PRINT:
                debug_print(__FILE__,__LINE__,__func__,"NT_PRINT node");
                exec_node(n->left);
                printf("print '%d'",get_node_ret(n->left));
                break;
            case NT_ASSIGNMENT:
        	    debug_print(__FILE__,__LINE__,__func__,"NT_ASSIGNMENT node");
                exec_node(n->left);
                exec_node(n->right);
                var[n->left->i - 'a'] = get_node_ret(n->right);
                break;
            case NT_STATEMENT_BLOCK:
        	    debug_print(__FILE__,__LINE__,__func__,"NT_STATEMENT_BLOCK node");
                exec_node(n->left);
                exec_node(n->right);
                break;
            case NT_PROGRAM:
        	    debug_print(__FILE__,__LINE__,__func__,"NT_PROGRAM node");
                break;
        }
    
        return 0;
    }
    内存释放
    int free_node(t_node *n){
        if( n != NULL ) {
            free_node(n->left);
            free_node(n->right);
        }
        free(n);
        return 0;
    }
    工具函数debug_print
    void debug_print(const char *fname, int lineno, const char *fxname, const char *debug_info){
        #ifdef cal_DEBUG
        printf("\n debug: enter at line %d in %s,function: %s info: %s\n",
            lineno,
            fname,
            fxname,
    		debug_info
            );
    	#endif
    }

    补充语法文件的Action部分

    %%
    program:  statement_block
                {
                    exec_node($1);
                    free_node($1);
                    printf("\n job done! \n");
                }
    ;
    
    statement_block: %empty
                {
                    $$ = build_node(
                            NT_STATEMENT_BLOCK,
                            NULL,
                            NULL,
                            NULL,
                            0
                            );
                    debug_print(__FILE__,__LINE__,__func__,"");
                }
    
            | statement_block statement
                { $$ = build_node(
                        NT_STATEMENT_BLOCK,
                        $1,
                        $2,
                        NULL,
                        0
                        );
    
                    debug_print(__FILE__,__LINE__,__func__,"");
                }
    ;
    
    statement: assignment { $$ = $1; }
            | print_func  { $$ = $1; }
            | if_block    { $$ = $1; }
            | while_block { $$ = $1; }
    ;
    
    if_block: IF '(' bool_expr ')' '{' statement_block '}'
                {
                    $$ = build_node(
                            NT_IF,
                            $3,
                            $6,
                            NULL,
                            0
                            );
                }
    
    while_block: WHILE '('  bool_expr ')' '{' statement_block '}'
                {
                    $$ = build_node(
                            NT_WHILE,
                            $3,
                            $6,
                            NULL,
                            0
                            );
                }
    
    assignment: VAR_NAME '=' expression ';' { $$ = build_node(
                                                        NT_ASSIGNMENT,
                                                        build_node(NT_VAR_NAME,NULL,NULL,NULL,(int)$1),
                                                        $3,
                                                        NULL,
                                                        0);
                                            }
    
    print_func : PRINT expression ';'   {  $$ = build_node(NT_PRINT,$2,NULL,NULL,0); }
    
    bool_expr: expression GT expression {  $$ = build_node(NT_BOOL_EXPR_GT,$1,$3,NULL,0);}
            |  expression LT expression {  $$ = build_node(NT_BOOL_EXPR_LT,$1,$3,NULL,0);}
    
    expression: INTEGER { $$ = build_node(NT_INTEGER,NULL,NULL,NULL,$1); }
            | VAR_NAME  { $$ = build_node(NT_VAR_NAME,NULL,NULL,NULL,(int)$1); }
            | expression O_ADD expression {  $$ = build_node(NT_O_ADD,$1,$3,NULL,0);}
            | expression O_MINUS expression  {  $$ = build_node(NT_O_MINUS,$1,$3,NULL,0);}
            | expression O_MULTIPLY expression {  $$ = build_node(NT_O_MULTIPLY,$1,$3,NULL,0);}
    
    
    %%

    cal.header.h

    enum NODETYPE{
        NT_STATEMENT,
        NT_IF,
        NT_WHILE,
        NT_PROGRAM,
        NT_STATEMENT_BLOCK,
        NT_O_ADD,
        NT_O_MINUS,
        NT_O_MULTIPLY,
        NT_INTEGER,
        NT_VAR_NAME,
        NT_BOOL_EXPR_GT,
        NT_BOOL_EXPR_LT,
        NT_PRINT,
        NT_ASSIGNMENT
    };
    
    typedef struct t_node{
        enum NODETYPE nt;
        struct t_node* left;
        struct t_node* right;
        struct t_node* rrnode;
        int i;  // for NT_INTEGER NT_VAR_NAME node
    }t_node;

    有了这些信息,就可以使用NODETYPE来构建,解析树了。在每次解析到对应节点或进行Reduction时,我们在语法文件的Action部分就可以调用一个build_node函数来构建对应的节点。我们可以看看如下的程序的解析树结构:

    i = 1 ;
    a = 0 ;
    while ( a < 100 ) {
        a = a + i;
        i = i + 1;
    }
    print i ;

    这个程序,可以找到,在自然数级数中,到第几项的时候,其和就超过了100。

    完整的代码

    最后是程序实现的部分,包括

    • build_node
    • execute_node
    • free_node
    • get_node_ret

    cal.l lex文件
    cat cal.l
    
    %{
        #include "cal.tab.h"
    %}
    %option noyywrap
    %%
    [[:digit:]]+ {
        yylval.a = atoi(yytext);
        return INTEGER;
    }
    
    [a-z] {
        yylval.c = yytext[0];
        return VAR_NAME;
    }
    
    "+" { return O_ADD;};
    "-" { return O_MINUS;};
    "*" { return O_MULTIPLY;};
    
    "while"  {return WHILE;}
    "if"  {return IF;}
    "print"  {return PRINT;}
    
    ">" {return GT;}
    "<" {return LT;}
    
    [();={}]  {return yylval.c = *yytext;}
    
    %%
    cal.header.h 头文件/数据结构定义
    cat cal.header.h
    
    enum NODETYPE{
        NT_STATEMENT,
        NT_IF,
        NT_WHILE,
        NT_PROGRAM,
        NT_STATEMENT_BLOCK,
        NT_O_ADD,
        NT_O_MINUS,
        NT_O_MULTIPLY,
        NT_INTEGER,
        NT_VAR_NAME,
        NT_BOOL_EXPR_GT,
        NT_BOOL_EXPR_LT,
        NT_PRINT,
        NT_ASSIGNMENT
    };
    
    typedef struct t_node{
        enum NODETYPE nt;
        struct t_node* left;
        struct t_node* right;
        struct t_node* rrnode;
        int i;  // for NT_INTEGER NT_VAR_NAME node
    }t_node;
    cal.y 语言语法文件
    cat cal.y
    %{
    
    #include <stdio.h>
    #include <stdlib.h>
    #include "cal.tab.h"
    #include "cal.header.h"
    
    // #define cal_DEBUG 1
    
    void debug_print(const char *fname, int lineno, const char *fxname, const char *debug_info){
        #ifdef cal_DEBUG
        printf("\n debug: enter at line %d in %s,function: %s info: %s\n",
            lineno,
            fname,
            fxname,
    		debug_info
            );
    	#endif
    }
    
    
    t_node* build_node(enum NODETYPE nt,t_node* left,t_node* right, t_node* r_right , int i){
        debug_print(__FILE__,__LINE__,__func__,"");
        t_node *t_n;
        t_n = NULL;
        t_n = (t_node *)malloc(sizeof(t_node));
        if (t_n == NULL){
            printf("Out of Memory\n");
            exit(1);
        }
    	t_n->nt = nt;
    	t_n->left = left;
    	t_n->right = right;
    	t_n->rrnode = r_right;
    	t_n->i = i;
        return t_n;
    }
    
    
    int var[26];
    
    int main (){
        int yydebug=1;
        yyparse();
        return 0;
    }
    
    void
    yyerror (char const *s)
    {
      fprintf (stderr, "something error: %s\n over", s);
    }
    
    
    int exec_node(t_node *n){
        if( n == NULL ) return 0;
        debug_print(__FILE__,__LINE__,__func__,"enter exec_node");
    
        switch(n->nt){
            case NT_INTEGER:
        		debug_print(__FILE__,__LINE__,__func__,"NT_INTEGER node");
    			break;
            case NT_VAR_NAME:
        		debug_print(__FILE__,__LINE__,__func__,"NT_VAR_NAME node");
    			break;
            case NT_O_ADD:
                exec_node(n->left);
                exec_node(n->right);
                n->i = get_node_ret(n->left) + get_node_ret(n->right);
        		debug_print(__FILE__,__LINE__,__func__,"NT_O_ADD node");
    			break;
            case NT_O_MINUS:
                exec_node(n->left);
                exec_node(n->right);
                n->i = get_node_ret(n->left) - get_node_ret(n->right);
        		debug_print(__FILE__,__LINE__,__func__,"NT_O_MINUS node");
                break;
            case NT_O_MULTIPLY:
        		debug_print(__FILE__,__LINE__,__func__,"NT_O_MULTIPLY node");
                exec_node(n->left);
                exec_node(n->right);
                n->i = get_node_ret(n->left) * get_node_ret(n->right);
                break;
            case NT_BOOL_EXPR_GT:
        		debug_print(__FILE__,__LINE__,__func__,"NT_BOOL_EXPR_GT node");
                n->i = 0;
                exec_node(n->left);
                exec_node(n->right);
                if (get_node_ret(n->left) > get_node_ret(n->right) )
                { n->i = 1 ; }
                break;
            case NT_BOOL_EXPR_LT:
        		debug_print(__FILE__,__LINE__,__func__,"NT_BOOL_EXPR_LT node");
                n->i = 0;
                exec_node(n->left);
                exec_node(n->right);
                if (get_node_ret(n->left) < get_node_ret(n->right) )
                { n->i = 1 ; }
                break;
            case NT_IF:
        		debug_print(__FILE__,__LINE__,__func__,"NT_IF node");
                exec_node(n->left);
                if (get_node_ret(n->left)){
                    exec_node(n->right);
                }
                break;
            case NT_WHILE:
        		debug_print(__FILE__,__LINE__,__func__,"NT_WHILE node");
                exec_node(n->left);
                while( get_node_ret(n->left)  ){
                    exec_node(n->right);
                    exec_node(n->left);
                }
                break;
            case NT_PRINT:
        		debug_print(__FILE__,__LINE__,__func__,"NT_PRINT node");
                exec_node(n->left);
                printf("print '%d'",get_node_ret(n->left));
                break;
            case NT_ASSIGNMENT:
        		debug_print(__FILE__,__LINE__,__func__,"NT_ASSIGNMENT node");
                exec_node(n->left);
                exec_node(n->right);
                var[n->left->i - 'a'] = get_node_ret(n->right);
                break;
            case NT_STATEMENT_BLOCK:
        		debug_print(__FILE__,__LINE__,__func__,"NT_STATEMENT_BLOCK node");
                exec_node(n->left);
                exec_node(n->right);
                break;
            case NT_PROGRAM:
        		debug_print(__FILE__,__LINE__,__func__,"NT_PROGRAM node");
                break;
        }
    
        return 0;
    }
    
    int get_node_ret(t_node *n){
        int r = n->i;
        switch(n->nt){
            case NT_VAR_NAME:
                r = var[n->i - 'a'];
                break;
        }
        return r;
    }
    
    int free_node(t_node *n){
        if(n != NULL){
            // printf("\n try to free memory of node %d \n",n->nt);
        }
        return 0;
    }
    
    
    %}
    
    %union {
        int a;  // for integer
        char c; // for var_name
        int int_bool; // for bool_expr
        struct t_node* t_n;
    }
    
    
    %type <t_n> expression bool_expr print_func assignment
    %type <t_n> while_block statement statement_block if_block
    
    %token <c> VAR_NAME
    %token <a> INTEGER
    
    %token O_ADD O_MINUS O_MULTIPLY
    
    %token GT LT
    
    %token WHILE IF
    %token PRINT
    
    %left O_ADD O_MINUS
    %left O_MULTIPLY
    
    %%
    program:  statement_block
                {
                    exec_node($1);
                    free_node($1);
                    printf("\n job done! \n");
                }
    ;
    
    statement_block: %empty
                {
                    $$ = build_node(
                            NT_STATEMENT_BLOCK,
                            NULL,
                            NULL,
                            NULL,
                            0
                            );
                    debug_print(__FILE__,__LINE__,__func__,"");
                }
    
            | statement_block statement
                { $$ = build_node(
                        NT_STATEMENT_BLOCK,
                        $1,
                        $2,
                        NULL,
                        0
                        );
    
                    debug_print(__FILE__,__LINE__,__func__,"");
                }
    ;
    
    statement: assignment { $$ = $1; }
            | print_func  { $$ = $1; }
            | if_block    { $$ = $1; }
            | while_block { $$ = $1; }
    ;
    
    if_block: IF '(' bool_expr ')' '{' statement_block '}'
                {
                    $$ = build_node(
                            NT_IF,
                            $3,
                            $6,
                            NULL,
                            0
                            );
                }
    
    while_block: WHILE '('  bool_expr ')' '{' statement_block '}'
                {
                    $$ = build_node(
                            NT_WHILE,
                            $3,
                            $6,
                            NULL,
                            0
                            );
                }
    
    assignment: VAR_NAME '=' expression ';' { $$ = build_node(
                                                        NT_ASSIGNMENT,
                                                        build_node(NT_VAR_NAME,NULL,NULL,NULL,(int)$1),
                                                        $3,
                                                        NULL,
                                                        0);
                                            }
    
    print_func : PRINT expression ';'   {  $$ = build_node(NT_PRINT,$2,NULL,NULL,0); }
    
    bool_expr: expression GT expression {  $$ = build_node(NT_BOOL_EXPR_GT,$1,$3,NULL,0);}
            |  expression LT expression {  $$ = build_node(NT_BOOL_EXPR_LT,$1,$3,NULL,0);}
    
    expression: INTEGER { $$ = build_node(NT_INTEGER,NULL,NULL,NULL,$1); }
            | VAR_NAME  { $$ = build_node(NT_VAR_NAME,NULL,NULL,NULL,(int)$1); }
            | expression O_ADD expression {  $$ = build_node(NT_O_ADD,$1,$3,NULL,0);}
            | expression O_MINUS expression  {  $$ = build_node(NT_O_MINUS,$1,$3,NULL,0);}
            | expression O_MULTIPLY expression {  $$ = build_node(NT_O_MULTIPLY,$1,$3,NULL,0);}
    
    
    %%

    编译与执行

    lex cal.l && \
    bison -d cal.y && \
    gcc cal.tab.c lex.yy.c -o a.out && \
    ./a.out < p.f.txt

    最后,需要注意,该程序更注重的是测试与实现,所以在“内存释放”可能会存在一些泄露的问题。

  • 个人的一些脚本和代码,经常会分散在不同的地方,管理起来并不方便,例如给WordPress编写的Plugin、测试MySQL时使用的一些脚本等,所以打算全部使用GitHub管理起来。对于个人使用,GitHub提供了私人仓库以存储代码,可以较为方便的管理一些还没有公开的个人代码。

    建立个人Git和GitHub环境

    GitHub CLI是一个具体简单交互式操作的命令行,可以完成与GitHub相关的一些交互与操作。对应的软件包/命令是gh

    安装gh-cli

    参考:Installing gh on Linux and BSD。Amazon Linux 2上安装:

    sudo yum-config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo
    sudo yum install gh

    使用gh配置GitHub授权

    接着,就可以使用gh auth login命令来进行GitHub的认证了(gh cli manual)。这是一个简单的交互式命令,这里使用https+token的方式完成认证(也可以使用浏览器的方式辅助完成命令行认证):

    gh auth login
    ? What account do you want to log into? GitHub.com
    ? What is your preferred protocol for Git operations on this host? HTTPS
    ? Authenticate Git with your GitHub credentials? Yes
    ? How would you like to authenticate GitHub CLI? Paste an authentication token
    Tip: you can generate a Personal Access Token here https://github.com/settings/tokens
    The minimum required scopes are 'repo', 'read:org', 'workflow'.
    ? Paste your authentication token: *********************************************************************************************
    - gh config set -h github.com git_protocol https
    ✓ Configured git protocol
    ! Authentication credentials saved in plain text
    ✓ Logged in as orczhou

    关于Token的配置与获取,可以参考:GitHub->Settings->Developer Settings ,这里不再详述。注意,Token意味着分配的所有的仓库权限,必须妥善保管,否则可能会带来巨大的安全隐患。

    如果要登出的话,则可以简单的使用如下命令:

    gh auth logout

    在本地pull与push仓库

    • 首先,在git中配置本地身份(用户名与)
    git config --global user.name "orczhou"
    git config --global user.email "orczhou@orczhou"
    • 首先,新建一个本地模板,并使用git命令初始化
    mkdir  terraform && cd terraform
    git init
    • 配置远端(remote)分支;并拉取远端代码
    git remote add origin https://github.com/orczhou/cloud-mysql-benchmark.git
    git pull origin main

    向远端push代码

    这时,如果修改了仓库中的代码,则可以使用push命令向远端发起提交请求。

    修改、测试并本地提交代码:

    vi gcp_rds_ins/all_in_one/README.md
    git add gcp_rds_ins/all_in_one/README.md
    git commit -m "gcp readme updated"

    向远端push修改:

    git push -u origin main

    该操作会向远端仓库的main分支,提交代码。

    向main分之合并代码

    可以在GitHub仓库页面,对比并创建一个pull request。

    发起pr之后,代码仓库则可以进行merge操作,将代码合并到main分之。

    在新增远程代码库(origin)

    git remote add origin https://github.com/orczhou/testing-delete-repo-if-u-like.git

    将本地代码,提交到远程代码库(origin)的main分支:

    git push -u origin main

    上面的,-u origin main ,其中-u参数表示push的目标代码库-u | --set-upstream

    在现有仓库的main分之上开发

    经常需要做这个动作,常用的命令比较简单,这里记录如下:

    mkdir repo_bench && cd repo_bench
    git init
    git branch -M main
    git remote add origin https://...
    git pull origin main
    
    

    直接修改本地main中的代码并提交到源端:

    cat "..." > README.md
    git add README.md
    git commit -m "first commit" --dry-run
    git commit -m "first commit"
    git push -u origin main

    使用gitignore忽略文件

    在代码开发过程中,由于编译、运行等原因会产生很多的中间文件,而这些中间文件时无需提交到代码仓库的。这时候,需要使用gitignore来忽略这部分文件。详细完整的gitignore的使用可以参考man gitignore

    常用的gitignore是可以通过.gitignore文件来实现,即在代码的根目录中新建该文件,那么在代码处理时,就会根据根据该文件的规则进行忽略。例如Terraform脚本常用的gitignore文件可以参考:

    所以,一个Terraform脚本的.gitignore可以依次作参考:

    # Compiled files
    *.tfstate
    *.tfstate.backup
    *.tfstate.lock.info
    
    # Directories
    .terraform/
    .vagrant/
    
    # SSH Keys
    *.pem
    
    # Ignored Terraform files
    *gitignore*.tf

    master分支与main分支

    在搜索git/github的相关资料的时候,经常还会搜索到master分支作为主分支的资料或者仓库。在2020年的George Floyd的案件发生后,美国的Black_Lives_Matter运动达到了前所未有的高度,最终也影响到在计算机领域的master/slave 一词的使用。更多的参考:Renaming the default branch from master@GitHubWhy GitHub renamed its master branch to main@theserverside

    不过,git在本地默认还是使用默认的master分支,所以如果没有手动切换分支,则还是会经常“默认的”创建master分支。

    查看未提交的修改

    git面向的场景就是分布式、多任务的开发代码管理,其独特的”three tree“模型可以很巧妙的实现这些能力。这也给初学者带来了很多理解上的障碍。

    git diffgit diff HEAD

    如果,想要查看自上次commit以来的所有变更,则需要试用git diff HEAD命令,通常HEAD指向的是,最后一次commit时的位置。

    # diff between "working" and "staging"
    git diff
    # diff between "working" and "repository"
    git diff HEAD
    # diff between "staging" and "repository"
    git diff --cached

    同步远程更新

    个人代码仓库管理中,有时候会有这样的情况:直接在远程仓库中修改了一些文件,然后如何让本地和远程保持同步。考虑这样的场景:直接在GitHub上对README.md文件进行了编辑,那么本地代码仓库如何保持这个更新。

    当然,这样做,通常可能会很危险:可能会覆盖掉你本地所做的更改,但是基于上面的场景,所以,有时候会需要这么做。Stackoverflow上有几个相关的问题,非常详细的介绍了做法:

    这里的推荐做法是这样,如果本地仓库的修改确定不要了(通常这是很危险的):

    git pull

    如果本地仓库修改都还需要:

    git stash
    git pull 
    git stash pop

    还可以:

    • 先使用 git fetch更新origin/main
    • 然后使用git diff main origin/main查看本地与远程的差异
    • 最后使用git mergeorigin/main与本地合并,并保持在本地

    这样origin/main是最新的,且本地分支也是最新的了

    git fetch
    git diff main origin/main
    git merge

    参考链接