PostgreSQL + QueryCache

last update: 16 Dec 2005

contents |index |previous |next

アルゴリズム + データ構造 = プログラム



3.1 アルゴリズム

    3.1.1 アルゴリズムの概略

    おおざっぱなアルゴリズム、というか処理フローは次のとおり:
    
    


      3.1.1.1 LOCKについて

      ここで、LOCKについて一言:
      新たに"QueryCacheLock"というLightWeightLockを追加した。

      	…
              TwoPhaseStateLock,
      
              NumFixedLWLocks,                        /* must be last except for MaxDynamicLWLock */
      #ifdef _QUERY_CACHE
              QueryCacheLock,
      #endif
              MaxDynamicLWLock = 1000000000
      } LWLockId;
      

      src/include/storage/lwlock.hの一部


      上の図に示したように、このLOCKは共有メモリ上のキャッシュ情報の読み書きに関する排他制御を行なうために使う。

    3.1.2 src/backend/tcop/postgres.c の改造ポイント

    postgres.cのexec_simple_query()内で、パースを終了した段階で、クエリを検査する関数:qc_check_query ()を実行する。

                    commandTag = CreateCommandTag(parsetree);
    #ifdef _QUERY_CACHE
                    if (query_cache && query_cache_local)
                      if (qc_check_query (commandTag, query_string) == true)
                        goto QUERYCACHE; /* I don't hesitate using "GOTO". */
    #endif
    

    exec_simple_query()の一部(1)


    そして、もしもデータがキャッシュされていたなら、一気にfinish_xact_command()の直前までGOTOでジャンプする。
    キャッシュされてなかったり、DELETE,UPDATE,INSERTだったら通常の処理を行なう。
            /*
             * Close down transaction statement, if one is open.
             */
    #ifdef _QUERY_CACHE
            if (query_cache && query_cache_local)
              {
                pq_flush ();
                qc_operate_cache ();
    
                _check_query_cache ("");
              }
    #endif
    
            finish_xact_command();
    

    exec_simple_query()の一部(2)


    3.1.3 src/backend/parser/parse_clause.c の改造ポイント

    parse_clause.cの改造ポイントは2箇所。

    setTargetTable()とtransformTableEntry()の間に挿入されたqc_set_oid()によって、SELECTやDELETE,UPDATE,INSERTに関わるテーブル=oidを、 ローカルメモリ上のメモリ領域(current_query)に保存していく。

    int
    setTargetTable(ParseState *pstate, RangeVar *relation,
    			   bool inh, bool alsoSource, AclMode requiredPerms)
    {
    	RangeTblEntry *rte;
    	int			rtindex;
    
    	/* Close old target; this could only happen for multi-action rules */
    
    	…
    
    
    	/*
    	 * Now build an RTE.
    	 */
    	rte = addRangeTableEntryForRelation(pstate, pstate->p_target_relation,
    										NULL, inh, false);
    	pstate->p_target_rangetblentry = rte;
    
    
    #ifdef _QUERY_CACHE
    	if (query_cache && query_cache_local)
    	  qc_set_oid (rte->relid);
    #endif
    
    	/* assume new rte is at end */
    	rtindex = list_length(pstate->p_rtable);
    	Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
    

    parse_clause.cの一部(1)


    static RangeTblEntry *
    transformTableEntry(ParseState *pstate, RangeVar *r)
    {
    	RangeTblEntry *rte;
    
    #ifdef _QUERY_CACHE
    	/**
    	 * Processing is possible even with 
    	 * transformFromClause(ParseState *pstate, List *frmList) 
    	 */
    	if (query_cache && query_cache_local)
    	  {
    	    Oid oid = RangeVarGetRelid (r, false);
    	    qc_set_oid (oid);
    	  }
    #endif
    	/*
    	 * mark this entry to indicate it comes from the FROM clause. In SQL, the
    	 * target list can only refer to range variables specified in the from
    	 * clause but we follow the more powerful POSTQUEL semantics and
    	 * automatically generate the range variable if not specified. However
    	 * there are times we need to know whether the entries are legitimate.
    	 */
    	rte = addRangeTableEntry(pstate, r, r->alias,
    							 interpretInhOption(r->inhOpt), true);
    

    parse_clause.cの一部(2)


    3.1.4 src/backend/libpq/pqcomm.c の改造ポイント

    pqcomm.c の改造ポイントも2箇所。

      3.1.4.1 データの保存

      qc_get_data_from_buffer_to_current_query ()という長い名前の関数によって、 internal_flush()のsecure_write()がまさにクライアントに送ろうとしているバッファ(PqSendBuffer)内のデータを ローカルメモリ上のメモリ領域(current_query)に保存していく。

      static int
      internal_flush(void)
      {
      	static int	last_reported_send_errno = 0;
      
      	char	   *bufptr = PqSendBuffer;
      	char	   *bufend = PqSendBuffer + PqSendPointer;
      
      #ifdef _QUERY_CACHE
      	/* ************************************************
      	 * ************** NOT SAFE ! REWRITE ! ************
      	 * ************************************************/
      	if (query_cache && query_cache_local)
      	  qc_get_data_from_buffer_to_current_query (PqSendBuffer, PqSendPointer);
      #endif
      
      	while (bufptr < bufend)
      	{
      		int			r;
      
      		r = secure_write(MyProcPort, bufptr, bufend - bufptr);
      
      
      		…
      
      

      pqcomm.cの一部(1)


      3.1.4.2 キャッシュデータの送信

      qc_pq_flush ()というグローバル関数を用意し、querycache.c内send_cache_data()から呼び出す。

      なぜ、グローバル関数を定義したかというと、 バッファ:PqSendBufferに直接書き込むには、こうするしかなかったから。 まさかPqSendBufferをグローバル変数にはできないし。

      #ifdef _QUERY_CACHE
      
      int qc_pq_flush (const unsigned char *data, int len);
      
      
      int
      qc_pq_flush (const unsigned char *data, int len)
      {
        if (query_cache && query_cache_local)
          {
            PqSendPointer = len;
            memcpy ((void *)PqSendBuffer, (const void *)data, len);
            return pq_flush ();
          }
        return 0;  /* dummy */
      }
      
      #endif
      

      pqcomm.cの一部(2)


3.2 データ構造

    3.2.1 postmasterが生成する共有メモリ上のデータ構造

    QueryCacheのデータは共有メモリ上に保存するが、 それらは3つのパートから成る。

    それぞれ、思い付きでつぎはぎしながら作ったので、 プログラムの見通しはかなり悪い。

      3.2.1.1 ヘッダー

      ヘッダー部分には、統計情報と、OID情報(oidと関連するSELECT文の情報[qid])へのポインタとなるハッシュテーブル、 およびクエリー情報(SECECT文と検索結果)へのポインタとなるハッシュテーブルである。

      /*--------------------------------------------
       * query cache header (postmaster)
       /-------------------------------------------*/
      
      struct hash_tbl {
        int num;                     /* いくつのデータが保存されているか */
        bid next;                    /* 保存されている最初のブロック番号 */
      };
      
      struct qc_space_header {
        bid block_num;               /* the number of blocks which are used.          */
        bid max_block_num;           /* the number of blocks which it prepares.       */
        hash_tbl ht[HASH_SIZE];      /* hash table.                                   */
      
        bid next_free_block;         /* link to the next free block.                  */
                                     /* if (bid)Nil is that there is not free blocks. */
      };
      
      struct QueryCache_Header {
        /*
         * 
         */
        unsigned long int qc_all_query; /* Number of all queries, include delete, update, insert ...     */
        unsigned long int qc_store;     /* Number of select queries which are stored data to the cache.  */
        unsigned long int qc_not_cached;
        unsigned long int qc_hits;      /* Number of select queries which hit to data on the cache.      */
      
        /*
         *
         */
        qc_space_header qc_oid_space;    /* QueryCache Table Shmem Space Section */
        qc_space_header qc_query_space;    /* QueryCache Query Shmem Space Section */
      };
      

      query cacheのヘッダー定義


      3.2.1.2 OID情報の保存

      qc_oid_spaceには、oidと、それを使ったSELECT文の番号qid(query id : qc_query_apaceでのID)を保存する。

      qc_oid_spaceには、struct oblockというブロックが連続している。 oblockにはID=toidがあるが、これは1から数える。 最大数はパラメータquery_cache_oblock_numで設定。

      typedef enum {
        FREE,      /* 0:  Free block   */
      
        /*  QueryCache Table Shmem Space                */
        TID,       /* 1:               */
        TQID,      /* 2:               */
      
        /*  QueryCache Query Shmem Space                */
        QID,       /* 3: Query         */
        DID        /* 4: Data          */
      } block_type;
      
      
      /*-------------------------
       * table qc_space (postmaster)
       /------------------------*/
      
      struct oblock {
        // bid  toid;             /* virtual index                              */
      
        block_type type;          /*                                            */
      
        union data {
          struct Tid {
            Oid oid;              /* relation ( = table ) number of PostgreSQL. */
          } tid;
          
          struct TQid { 
            hkey key;             /* to search for query_qc_space.                 */
            bid tqid;             /* link to query id at query_qc_space.           */
            bid prev, next;
          } qid;
        };
      
        bid prev, next;
      };
      


      3.2.1.3 クエリと結果保存

      qc_query_spaceには、SELECT文の情報と、その実行結果を保存する。

      qc_query_spaceには、struct qblockというブロックが連続している。 qblockにはID=qidがあるが、これは1から数える。 最大数はパラメータquery_cache_qblock_numで設定。

      /*-------------------------
       * query qc_space (postmaster)
       /------------------------*/
      
      typedef enum query_atr {
        QC_QATR_NORMAL,      /* 0:  Normal query                    */
        QC_QATR_HUGE         /* 1:  Result was Too Huge             */
      } query_atr;
      
      
      struct qblock {
        // bid qid;               /* virtual index                               */
      
        block_type type;          /*                                             */
      
        union unit {
          struct Qid {
            hkey key;             /*                                             */
            unsigned char query[PQ_QUERY_LEN];  /* クエリを保存 */
            query_atr attr;       /*                                             */
      
            char user_name[PQ_USER_NAME_LEN]; /*                                             */
            char db_name[PQ_DB_NAME_LEN];     /*                                             */
      
            bid toid[MAX_OID];        /* 関連するoidを保存                           */
            bid tqid[MAX_OID];        /* 関連するoid情報を保持するqc_oid_spaceにおいて、*/
      				/* oidからリンクされているtqidの番号を保存        */
      				/*  qc_query_spaceからqc_oid_spaceへのポインタ    */
            int tid_num;              /*                                             */
      
            int data_num;
            bid next;			/* 送信データへのポインタ                   */
            bid last;			/* 一番最後の送信データブロックへのポインタ */
          } qid;
          
          struct Did {
            unsigned char data[QBLOCK_DATA_SIZE];  /* 送信データを保存 */
            int data_len;
            bid next;
          } did;
        };
        
        bid prev, next;           /*                                             */
      };
      

      クエリと結果保存


    3.2.2 各postgresが持つデータ構造

    共有メモリ上のデータを操作(追加、削除)する前に、 一時的にクエリや結果データを保持する構造体。

    /*--------------------------------------------
     * current_query (postgres)
     /-------------------------------------------*/
    
    typedef enum query_cond {
      QC_NULL,       /* 0: null                  */
      QC_STORE_DATA, /* 1: SELECT and No-Cahce   */
      QC_SEND_CACHE, /* 2: SELECT and Cached     */
      QC_DELETE_CACHE, /* 3: Drop Cache Data     */
      QC_THROUGH,    /* 4: Drop Cache Data     */
      QC_END,        /* 5:                       */
      QC_INVALID,    /* 6: Query Cache InValisd  */
      QC_TOO_HUGE,   /* 7: Result is Too Huge    */
      QC_NO_CACHE,   /* 8: Don't cache!          */
      QC_COMMIT      /* 9: Transaction -> COMMIT */
    } query_cond;
    
    
    typedef struct data_cell {
      int data_num;
      unsigned char data[MAX_DATA][PQ_BUFFER_SIZE];
      int data_len[MAX_DATA];
    
      struct data_cell *next;
    } data_cell;
    
    
    typedef struct query {
      bid qid;
    
      /* query string section */
      hkey key;
      unsigned char query_string[PQ_QUERY_LEN];
      char commandTag[32];
      char user_name[PQ_USER_NAME_LEN];
      char db_name[PQ_DB_NAME_LEN];
    
      /* codition */
      query_cond cond;
    
      /* attribute */
      query_atr attr;
    
      /* related tables */
      Oid oid[MAX_OID];
      int oid_num;
    
      /* result data section */
      int total_data_num;
      struct data_cell *next;
      struct data_cell *last;
    } query;
    
    /*--------------------------------------------
     *  (postgres)
     /-------------------------------------------*/
    
    struct oid_cell oid_cell;
    struct oid_cell {
      Oid oid;
      struct oid_cell *next;
    } oid_cell;
    
    typedef struct Tx_info {
      struct oid_cell *next;
    } Tx_info;
    
    

    クエリとデータを一時的に保存するデータ構造



contents |index |previous |next


since 04/Oct/2004