Your SlideShare is downloading. ×
mysqlnd query cache plugin: user-defined storage handler
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

mysqlnd query cache plugin: user-defined storage handler

2,089
views

Published on

User-defined storage handler are the way to lift most limitations of the query cache plugin for mysqlnd. For example, you can break out TTL invalidation and put any other more complex invalidation in …

User-defined storage handler are the way to lift most limitations of the query cache plugin for mysqlnd. For example, you can break out TTL invalidation and put any other more complex invalidation in place. You may go as far as preventing stale results from being saved. Learn how!

Published in: Technology

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,089
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
16
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. MySQL native driver for PHP: Customizing the query cache
  • 2. mysqlnd_qc: Customization by user defined storage handler Ulf Wendel, Andrey Hristov MySQL Connectors Team Sun Microsystems
  • 3. Table of Contents
    • Overview
      • How to customize: template pattern
      • 4. Flavours of user handler
    • Handler API
      • Storage
      • 5. Registration
    • Examples
      • Quick start: identify cache candidates
      • 6. Master class: slam defense
  • 7. Breaking the limits
    • Cache decision
      • Shall this query be cached: is_select()
      • 8. Filter by content, run or store time, result set size
    • Cache storage
      • Storage location
      • 9. Scope
    • Replacement strategy
      • Limiting cache storage size
    • Extending monitoring capabilities
  • 10. Template Method pattern, kind of Core: invariant part of the caching, handler: variant part <?php /* Any PHP MySQL application */ ?> ext/*mysql* ext/*mysql* mysqlnd mysqlnd Query Cache Plugin (mysqlnd_qc core) Storage handler (mysqlnd_qc)
  • 11. Variant and invariant parts (Miss/Put) mysqlnd Cache Plugin handler Cache Plugin core query() Should cache? Is cached? Activate data recorder Call original query() Deactivate recorder Cache wire data is_select() Want to cache statement? find() Available? TTL? Slam defense? Statistics? add() Run time? Data size? Replacement strategy?
  • 12. Redefine everything or selected parts
    • Choose: quick change or complex algorithm?
    • 13. Full control through new user handler
      • Procedural: mysqlnd_qc_set_user_handlers()
      • 14. OOP: interface mysqlnd_qc_handler
    • Redefining selected invariants
      • OOP: extend class mysqlnd_qc_handler_default
      • 15. No other build-in handler exported as user class
    • All approaches use almost the same handler API
  • 16. Table of Contents
    • Overview
      • How to customize: template pattern
      • 17. Flavours of user handler
    • Handler API
      • Storage
      • 18. Registration
    • Examples
      • Quick start: identify cache candidates
      • 19. Master class: slam defense
  • 20. Handler API overview
    • Almost the same for all kinds of user handler
      • get_hash_key($host, $port, $user, $db, $query)*
      • 21. find_in_cache($key)
      • 22. return_to_cache($key)
      • 23. add_to_cache($key, $data, $ttl, $run_t, ...)
      • 24. is_select($query)
      • 25. update_stats($key, $run_t, $store_t)
      • 26. get_stats()
      • 27. clear_cache()
      • 28. init()*, shutdown()*
  • 29. Handler API – Cache Put (I) Application Cache Plugin handler Cache Plugin core *query() Should cache? Yes! Cache entry 'key'? is_select(...) a) Don't cache - return: false b) Cache it – return: int TTL get_hash_key(...) Return key of cache entry. find_in_cache(...) Returns NULL because not found. Do we have 'key'?
  • 30. Handler API – Cache Put (II) Application Cache Plugin handler Cache Plugin core *query() add_to_cache(...) Add to cache if not exists. Return true if added, false if already in cache. Activate data recorder Call original query() Deactivate recorder Cache wire data *store _result() (often implicit)
  • 31. Handler API – Cache Hit (I) Application Cache Plugin handler Cache Plugin core *query() Should cache? Yes! Cache entry 'key'? is_select(...) a) Don't cache - return: false b) Cache it – return: int TTL get_hash_key(...) Return key of cache entry. find_in_cache(...) Search cache entry, check if still valid, return cache entry. Do we have 'key'?
  • 32. Handler API – Cache Hit (II) Application Cache Plugin handler Cache Plugin core *store _result() (often implicit) Client served! Record timings! return_to_cache(...) Cache entry no longer in use by core. (Default needs this) update_stats(...) Run and store time recorded and reported by the core, useful for per-entry stats.
  • 33. Handler API – Cache Miss (I) Application Cache Plugin handler Cache Plugin core *query() Should cache? No! is_select(...) a) Don't cache – return: false b) Cache - see Cache Put (I)
  • 34. API details - get_hash_key(...)
    • get_hash_key($host, $port, $user, $db, $query)
      • string $host – MySQL Server host info
      • 35. string $port – MySQL Server port
      • 36. string $user – MySQL user
      • 37. string $db – MySQL data base
      • 38. string $query – SQL statement
    • Returns a string that serves as a 'key' for a cache entry identified by the given connection parameters and query string.
  • 39. API details – get_hash_key(...)* pitfall
    • mysqlnd_qc_handler_default:: get_hash_key($host, $port, $user, $db, $query, $persistent)
      • string $host – MySQL Server host info
      • 40. string $port – MySQL Server port
      • 41. string $user – MySQL user
      • 42. string $db – MySQL data base
      • 43. string $query – SQL statement
      • 44. bool $persistent – Flag persistent connection
    • You must not change $persistent when calling build-in method! PHP will crash if you do.
  • 45. API details - find_in_cache(...)
    • find_in_cache($key)
      • string $key – Key of the requested cache entry
    • Returns the cache entry associated with the 'key' or NULL.
    • 46. This is an ideal place to check not only if a cache entry is available but also to implement tife-to-life (TTL) checks or slam defense logic.
    • 47. For example, if you can find the cache entry but it has expired, you should return NULL to trigger a cache miss.
  • 48. API details - find_in_cache(...)
    • The build-in slam defense logic of the default and APC handler is implemented through find_in_cache() and add_to_cache(). If a cache entry can be found but is expired the cache entry is not returned to the core but not removed from cache either. The caller will experience a cache miss and attempt to update the cache entry through add_to_cache() later on. Meanwhile, if another client tries to access the cache entry, the stale cache entry will be served to the other client (slam hit). At some point the original caller will cause add_to_cache() to be called and the cache entry can be refreshed.
  • 49. API details - return_to_cache(...)
    • return_to_cache($key)
      • string $key – Key of the cache entry
    • Message from the core to the storage handler that the core no longer uses the cache entry returned by find() because a cache hit has been completed.
    • 50. Hardly any pratical meaning to userland handler.
    • 51. Relevant for C based handler that work with references, such as the default handler does. See also mysqlnd_qc.std_data_copy for default handler configuration details.
  • 52. API details - add_to_cache(...)
    • add_to_cache($key, $data, $ttl, $run_t, $store_t, $row_c)
      • string $key – Key of the cache entry
      • 53. string $data – Binary wire data to cache
      • 54. int $ttl – TTL of the cache entry (s)
      • 55. int $run_t – Run time of uncached query (ms)
      • 56. int $store_t – Store time of uncached query (ms)
    • Message from the core to the handler to create a new cache entry with the given data. Returns true, if the cache entry has been created and false, if the cache entry already exists.
  • 57. API details - add_to_cache(...)
    • TTL is forwarded from is_select() call
    • 58. Timings can be used to build per-entry performance figures such as run time comparisons of the cached and uncached query
    • 59. If the user storage handler makes use of a cache medium that persists over multiple web requests it can happen that two web requests add the same key to the cache almost simultanously – one will be faster. To get the core statistics for cache hits and cache misses right, you can return true or false. See also statistics presentation!
  • 60. API details - is_select(...)
    • is_select($query)
      • string $query – SQL statement
    • Returns false if the given query shall not be cached. Returns 0 if it shall be cached and TTL shall be equal to mysqlnd_qc.ttl. Returns an integer representing a TTL, if the query shall be cached but a custom TTL is to be used.
    • 61. Note that you may have to parse the SQL to catch SQL hints that specify the TTL
    • 62. Note the core logic TTL(0) != endless
  • 63. API details - is_select(...)
    • For build-in storage handler the TTL is always measured in seconds. TTL interpretation is subject to storage handler, e.g. in find() to check if a cache entry is still valid. A user handler may interpret the TTL value, for example, as milliseconds. This is perfectly valid as long as you are aware of the potential differences between your own handler and the build-in storage handler and you stay within the limits of integers and you respect the special meaning of the value “0.” The core will transparently forward your TTL setting to add().
  • 64. API details - update_stats(...)
    • update_stats($key, $run_t, $store_t)
      • string $key – Key of the cache entry
      • 65. double $run_t – Run time of the cached query
      • 66. double $store_t – Store time of the cached query
    • Run and store time recorded by the core.
    • 67. Can be used to maintain per-entry cache statistics.
  • 68. API details - clear_cache()
    • clear_cache()
    • 69. Flush all cache entries. Called by the core if the user calls mysqlnd_qc_clear_cache().
  • 70. API details - get_stats()
    • get_stats()
    • 71. Returns an array of cache statistics and arbitrary other data which will become part of return value of mysqlnd_qc_get_cache_info().
    • 72. mysqlnd_qc_get_cache_info() returns a hash. The return value of get_stats() will be added to the hash using the key “data”. It is recommended to align the return value of get_stats() with the “data” hash provided by the build-in handlers, in particular Default and APC.
  • 73. Procedural user storage handler void mysqlnd_qc_set_user_handlers( string get_hash_key, string find_query_in_cache, string return_to_cache, string add_query_to_cache_if_not_exists, string query_is_select, string update_cache_stats, string get_stats, string clear_cache ) There is also an OO API to please you – see below. The OO API has additional function callbacks! The OO API is likely to become the future standard. mysqlnd_qc_set_user_handlers()
  • 74. Registering OO user storage handler bool mysqlnd_qc_change_handler (mysqlnd_qc_default_handler handler) bool mysqlnd_qc_change_handler(string handler) Changes the storage handler. Returns false if the current handler cannot be shutdown or the requested handler cannot be initialized. Failing to change the handler should be considered as a fatal error unless the change fails because the requested handler is unknown. You can either change the storage handler to one of build-in handlers or register a user-defined storage handler object derived from mysqlnd_qc_handler_default.
  • 75. Handler API – Handler registration (I) Active handler App / QC Core mysqlnd_qc_change_handler() shutdown active handler: OK! shutdown() return true New handler init() return true init new handler: OK! install to new handler return true
  • 76. Handler API – Handler registration (II) Active handler App / QC Core mysqlnd_qc_change_handler() shutdown active handler: OK! shutdown() return false New handler init() return true init new handler: OK! install to new handler Warning: Shutdown of previous handler '%s' failed return true
  • 77. Handler API – Handler registration(III) Active handler App / QC Core mysqlnd_qc_change_handler() shutdown active handler: OK! shutdown() return false New handler init() return false Warning: Error during changing handler. Init of '%s' failed use build-in “nop” handler Warning: Shutdown of previous handler '%s' failed cache disabled: return false
  • 78. API details - init()
    • init()
    • 79. Returns true if the handler is ready to be used. Called upon handler registration triggered by a call to mysqlnd_qc_change_handler().
    • 80. Not available with mysqlnd_set_user_handlers()!
    • 81. Part of the class mysqlnd_qc_handler_default
    • 82. Part of the interface mysqlnd_qc_handler
  • 83. API details - shutdown()
    • shutdown()
    • 84. Returns true if the handler has succeeded to clean up resources and is ready to be shutdown. Called upon handler registration triggered by a call to mysqlnd_qc_change_handler().
    • 85. Not available with mysqlnd_set_user_handlers()!
    • 86. Part of the class mysqlnd_qc_handler_default
    • 87. Part of the interface mysqlnd_qc_handler
  • 88. Table of Contents
    • Overview
      • How to customize: template pattern
      • 89. Flavours of user handler
    • Handler API
      • Storage
      • 90. Registration
    • Examples
      • Quick start: identify cache candidates
      • 91. Master class: slam defense
  • 92. Quick Start: subclassing
    • Subclass mysqlnd_qc_handler_default
    • 93. Advantages
      • Fast: replace selected invariants only
      • 94. Easy: no need to know all API calls
      • 95. Convenient: reuse internal C implementation
    • Disadvantages
      • Only build-in default handler can be subclassed
  • 96. Class mysqlnd_qc_handler_default class mysqlnd_qc_handler_default { public function init() {} public function is_select(...) {} public function get_hash_key(...) {} public function return_to_cache(...) {} public function add_to_cache(...) {} public function find_in_cache(...) {} public function update_cache_stats(...) {} public function get_stats(...) {} public function clear_cache() {} public function shutdown() {} }
  • 97. Quick start: search cache candidates Which queries does the app run? Which ones to cache? class qc_monitor extends mysqlnd_qc_handler_default { public function is_select($query) { printf(&quot;qc_monitor: '%s' &quot;, $query); return parent::is_select($query); } } $monitor = new qc_monitor(); mysqlnd_qc_change_handler($monitor); $mysqli = new mysqli(&quot;host&quot;, &quot;user&quot;, &quot;passwd&quot;, &quot;db&quot;); $mysqli->query(&quot;SELECT 1&quot;);
  • 98. Quick start: query monitoring (I) class qc_monitor extends mysqlnd_qc_handler_default { private $key_to_query = array(); public function get_hash_key($h, $p, $u, $d, $q, $p) { $key = parent::get_hash_key($h, $p, $u, $d, $q, $p); $this->key_to_query[$key] = $query; return $key; } public function is_select($query) { return true; } /* continued */
  • 99. Quick start: query monitoring (II) /* continued */ public function add_to_cache ($key, $data, $ttl, $run_t, $store_t, $row_c) { printf(&quot;Query = '%s' &quot;, $this->key_to_query[$key]); printf(&quot; run time = %d ms &quot;, $run_t); printf(&quot; store time = %d ms &quot;, $store_t); printf(&quot; size = %d rows &quot;, $row_c); /* do not add to cache! */ return false; } }
  • 100. Quick Start: Don't do this
    • Don't modify wire data!
      • Packets out of order. Expected n received m...
      • 101. MySQL server has gone away
      • 102. Error reading result set's header
    • Don't change $persistent of get_hash_key()!
      • Segmentation fault
      • 103. Internal Server Error 500
  • 104. Develop everything from ground up interface mysqlnd_qc_handler { public function is_select(...) {} public function get_hash_key(...) {} public function return_to_cache(...) {} public function add_to_cache(...) {} public function find_in_cache(...) {} public function update_cache_stats(...) {} public function get_stats(...) {} public function clear_cache() {} } No limits – basic usage pattern like before
  • 105. Cache Miss Client 2...100 Client 2...100 Client 2...100 Master class: Slam defense Serve stale data to avoid MySQL overloading Client 1 Client 2...n MySQL Client 2...100 Client 2...100 Client 2...100 Client 1 Client 2...n Slam Stale Hit MySQL Cache Hit
  • 106. Master class: slam defense
    • Short code examples
      • written to illustrate the algorithm
      • 107. but pointless because, ...
    • Slam defense is build-in to APC and Default!
      • mysqlnd_qc.slam_defense_ttl
    • Storage must survive multiple requests
      • No slamming, if only one process...
      • 108. $this->cache is not shared among PHP instances
      • 109. Default + CGI
  • 110. Master class: slam defense (I) class qc_monitor extends mysqlnd_qc_handler_default { private $cache = array(); public function add_to_cache( $key, $data, $ttl, $run_t, $store_t, $row_c) { printf(&quot;add : put(ttl = %d) &quot;, $ttl); $this->cache[$key] = array( &quot;data&quot; => $data, &quot;valid_until&quot; => microtime(true) + $ttl, &quot;slam_until&quot; => NULL ); return true; } /* continued */
  • 111. Master class: slam defense (II) public function find_in_cache($key) { if (!isset($this->cache[$key])) { printf(&quot;find: miss &quot;); return NULL; } $now = microtime(true); if ($this->cache[$key][&quot;valid_until&quot;] > $now) { printf(&quot;find: hit &quot;); return $this->cache[$key][&quot;data&quot;]; } /* continued */
  • 112. Master class: slam defense (III) if ($this->cache[$key][&quot;slam_until&quot;]) { if ($this->cache[$key][&quot;slam_until&quot;] > $now) { printf(&quot;find: hit, slam defense active &quot;); return $this->cache[$key][&quot;data&quot;]; } else { printf(&quot;find: miss, slam defense expired &quot;); unset($this->cache[$key]); return NULL; } } printf(&quot;find: expired, slam defense starts &quot;); $this->cache[$key][&quot;slam_until&quot;] = $now + 2; return $this->cache[$key][&quot;data&quot;]; } }
  • 113.
    • Presentations
      • A query cache plugin
      • 114. Query cache plugin benchmark impressions
      • 115. Dig deeper with QC statistics
      • 116. Developing user storage handler
    Further reading
  • 117. The End Feedback: ulf.wendel@sun.com The End Feedback: [email_address] , [email_address]

×