WL#6650: InnoDB_Memcached: support multiple get and range search
Affects: Server-8.0
—
Status: Complete
There have been requests on further enhance the InnoDB Memcached functionalities to cope with various scenario. In this worklog, we would implement 2 prominent ones. 1) We will add support of "multiple get" operation to further improve the read performance. This means user can fetch multiple key value pairs in a single memcached query. This is what HandlerSocket do when do their benchmarking. Our 1million QPS read only performance tests showed the bottleneck is now mostly shifting to client (for issuing/processing so many requests). With this "multiple get" option, it can alleviate frequent communication traffic between client and server. 2) Add support for "range queries". This request is by Yoshinori of Facebook. And this could further widen the usage scenario of InnoDB Memcached. With Range queries, user can specify a particular range, and fetch all the qualified values in this range. This provides something that far more than original memcached can support, and shows the flexibility of InnoDB APIs and InnoDB Memcached
Following are functional requirement for range get: 1. Be able to get correct value corresponding to each key in multiple get command 2. If there is no corresponding value for a key, such key/value is not returned in the result. But it does not affect other values to be reported 3. All the keys in the multiple get must belongs to the same table. We do not support switch table in a multiple get scenario. 4.It does not support multiple range get. Only one single range is allowed for get 5. The perl test client needs to be extended to support testing multiple get Following are functional requirement for range get: 1. Support 4 compare symbol (<, >, <= and >=), each needs to be preceded with a marker of "@" symbol: get @>D VALUE H 0 7 Hamburg END 2. One of less than (<) or less equal than(<=) symbol can be followed by a larger than (>) or large equal than (>=) symbol or vice versa to form an enclosed range get @>B@<D VALUE C 0 7 Cottbus END Above query reports values that are larger than "B" and less than "D" 3. The range query must start with symbol "@" and followed by compare symbols. If the key string does not start with "@", it will not treated as range query. If "@" is followed by "=", this is not consider as a compare operator, so the whole string start with "@=" will be consider as part of key. 4. only one less symbol (either < or <=) and one large symbol (> or >=) can be present in the query key string 5. Similarly the perl test client needs to be extended to support testing range get 6. There cannot be multiple individual range queries (as for using multi-key schema for range query). The query will end automatically after first range query. For example: get @<B @>D It will return only value corresponding to "@<B", and "@>D" is ignored. 7. If a compare operation is followed by table switch symbol "@@", the "@@" will not be consider as sign for table switching, instead it will be consider as part of key string. 8. For multi-get, there is no limitation in terms of number of keys enforced by InnoDB memcached. InnoDB memcached does enforce a limit on result memory of 128MB to store the result and key value pair. NF#1: For Performance Test, the multi-get should behave faster than (at least as fast as) that of single get.
Both the "multi-value get" and "range query" made easier by our current memcached design, which manages the result buffer ourselves. In this way, we could keep "multi-results" across searches without worrying result being overwritten. And in both case, we will avoid setting up search tuple/trx for each key value, and use the initial setup for all the searches. In this way, it will enhancement the performance over single search. 1) For multi-value get, the "get" command will specify the values it try to get in one single "get" call: We use an example to demo the use of multi-get and range search. For example, we have following table, column c1 is mapped to key, and c2 is the value: +-----+--------------+------+------+------+ | c1 | c2 | c3 | c4 | c5 | +-----+--------------+------+------+------+ | AA | HELLO, HELLO | 8 | 0 | 0 | | bb | 123 | 0 | 1 | 0 | | mm | www | 0 | 1 | 0 | | xxx | 3333 | 0 | 1 | 0 | +-----+--------------+------+------+------+ For multiple value search, if we want to get value for key that equals "aa", "bb" and "xxx": get aa bb xxx VALUE aa 8 12 HELLO, HELLO VALUE bb 0 3 123 VALUE xxx 0 4 3333 END The result will be return in sequence. It is possible that you could switch table with the first key, but you are not allow to switch table in subsequent key: For example, if you issue : get @@desct12.Koeln Oldenburg VALUE @@desct12.Koeln 0 7 O|Olden VALUE Oldenburg 0 11 O|Oldenburg END This will switch to table described by "desct12" 2) For range search, we will support single "<", ">", "<=", ">=" operators, signified by an "@" symbol. For example, if we have following table, mysql> select * from t1; +----+-----------+------+------+------+ | c1 | c2 | c3 | c4 | c5 | +----+-----------+------+------+------+ | B | Berlin | 0 | 0 | 0 | | C | Cottbus | 0 | 0 | 0 | | D | Darmstadt | 0 | 0 | 0 | | H | Hamburg | 0 | 0 | 0 | +----+-----------+------+------+------+ 4 rows in set (0.00 sec) if we want to search all values large than "C": get @>C VALUE D 0 9 Darmstadt VALUE H 0 7 Hamburg END Notice all key value large than "C" are returned. if we want to get values larger than "C" but smaller than H, you can do following: get @>C@<H VALUE D 0 9 Darmstadt END You could put the less than (@<) at the beginning too, the result is the same: get @<H@>C VALUE D 0 9 Darmstadt END There are maximum 2 compare symbols that will be parsed, one with less or less equal, one with larger or larger equal, the others are treated are part of key. For example, if you issue following command: get @<H@>C@>D VALUE D 0 9 Darmstadt END The third "@>D" is treated as part of key, so this clause will look for value smaller than "H" but larger than "C@>D", in which case "D" fits. 3) Affected Module The overall InnoDB memcached architecture and be categorized into following modules: [memcached client] <---network---> [memcached daemon]<->[innodb memcached] <-->[Innodb Storage] <----------- memcached plugin --------> As expected, the changed code is mainly in the "memcached plugin" part, and in the interfacing function of [memcached daemon]and [innodb memcached]. More specifically, it is in "process_get_command()" of Memcached Daemon and "innodb_get()" of InnoDB Memcached code. The "process_get_command()" will be responsible to loop through each key in multiple get query, and ask "innodb_get" to get all the corresponding value, and then append to the result set. This is the same as for the range query. Result are returned in key value pair sequence, if multiple key/value qualifies: get @>=B VALUE BB 0 14 KASI VISWANATH VALUE CC 0 17 PRAJWAL VISWANATH VALUE DD 0 16 NIKHILA VISWANAT END
1) 2 new bool flags are added in individual connection structure to indicating if this is a range search or multiple get: struct innodb_conn_data_struct { .... bool range; /*!< range search */ bool multi_get; /*!< multiple get */ } When these 2 bits are set, the connection data are held and the result buffer are kept until all results are fetched. 2) For range search, the range key are parsed in innodb_get(). Other than the original equal search, following additional modes are added: /** @enum ib_srch_mode_t InnoDB cursor search modes for ib_cursor_moveto(). Note: Values must match those found in page0cur.h */ typedef enum { IB_CUR_G = 1, /*!< If search key is not found then position the cursor on the row that is greater than the search key */ IB_CUR_GE = 2, /*!< If the search key not found then position the cursor on the row that is greater than or equal to the search key */ IB_CUR_L = 3, /*!< If search key is not found then position the cursor on the row that is less than the search key */ IB_CUR_LE = 4 /*!< If search key is not found then position the cursor on the row that is less than or equal to the search key */ } ib_srch_mode_t; 3) Memcached function process_get_command() is modified a bit to support range and multiple get feature. So it will continue loop to fetch each result, packaging it, and send to connection (add_iov). 4) InnoDB API function ib_read_tuple() is modified to add column value check (for scan), so when it do a range scan, it knows when to stop (when the search criteria no long matches). 5) Similarly in ib_read_tuple(), it also makes sure the result buffer is enough to hold all results. If not, it will automatically reallocates to extend the buffer. 6) InnoDB APIs function ib_cb_moveto() is modified to add a "direction" parameter. So when it can scan forward to get next rows for qualification.
Copyright (c) 2000, 2024, Oracle Corporation and/or its affiliates. All rights reserved.