{"id":74,"date":"2012-12-31T19:57:41","date_gmt":"2012-12-31T17:57:41","guid":{"rendered":"http:\/\/www.olivierdoucet.info\/blog\/?p=74"},"modified":"2013-01-09T17:43:27","modified_gmt":"2013-01-09T15:43:27","slug":"rrdtool-mysql","status":"publish","type":"post","link":"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/","title":{"rendered":"RRDTool with MySQL"},"content":{"rendered":"<p>A few months ago, I started some tests with RRDTool and a data source in MySQL. At that time, I just saw that performance was awful but did not investigate further. Now is time to check why, and if any improvements could be made.<!--more--><\/p>\n<h1>Convert existing data to SQL<\/h1>\n<p>First, let&rsquo;s see how to convert existing data to MySQL. You would need &lsquo;rrdtool dump&rsquo; + a tool that read data and insert it back to your database; The tool I wrote for this (in PHP) will be available in a few days on github (stay tuned). I&rsquo;ll talk later about how to design your database, and what are the improvements you can make.<br \/>\nThe key here is how you&rsquo;ll import the data : do not use INSERT INTO statement, this will take too much time (insert buffer is mono thread in MySQL and very inefficient when dealing with large amount of data). use LOAD DATA [LOCAL] INFILE instead.<\/p>\n<h1>Graphing<\/h1>\n<p>rrdtool has support for libdbi, that supports several databases system (MySQL, PostgreSQL, SQLite, FreeTDS &#8211; i.e MS SQL and FireBird).<\/p>\n<p>You need to change your command line of course. Here is an example.<br \/>\nBEFORE :<\/p>\n<pre>rrdtool graph - DEF:user=\/path\/to\/src.rrd:user:AVERAGE:step=600 AREA:user#66ff00:\"user\"<\/pre>\n<p>AFTER :<\/p>\n<pre>rrdtool graph - DEF:user=sql\/\/mysql\/host=127.0.0.1\/dbname=testrrd\/username=myuser\/password=pwd\/\/data\/*ts\/val\/idds=1:avg:AVERAGE:step=600 AREA:user#66ff00:\"user\"<\/pre>\n<p>Notes :<\/p>\n<ul>\n<li>please report to documentation to know the exact syntax and why I used these parameters.<\/li>\n<li>ts is prefixed with * because I use datetime format in SQL.<\/li>\n<li>DS is called \u00ab\u00a0avg\u00a0\u00bb because this is the aggregated function to be used. I select the REAL DS with the WHERE condition (\u00ab\u00a0idds=1\u00a0\u00bb).<\/li>\n<\/ul>\n<h1>Performances<\/h1>\n<p style=\"text-align: left;\">Performances are degraded, based on how many DS you used (one query to the DB will be made for each DS), and the length of the graph, <strong>exponentially<\/strong>.<a href=\"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/graph\/\" rel=\"attachment wp-att-75\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-75\" alt=\"graph\" src=\"https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/graph-300x202.png\" width=\"300\" height=\"202\" srcset=\"https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/graph-300x202.png 300w, https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/graph.png 637w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a>I wanted to know why such degradations. By looking at MySQL status, I can see that the query itself does not take more than 70ms, even if the graph itself needs nearly 2 seconds to be generated.<\/p>\n<p>I then used oprofile to know what took so much time.<\/p>\n<p>&nbsp;<\/p>\n<pre>samples| %|\r\n ------------------\r\n 14794 90.8443 libmysqlclient.so.16.0.0\r\n 441 2.7080 libc-2.12.so\r\n 277 1.7010 no-vmlinux\r\n 132 0.8106 libz.so.1.2.3\r\n 102 0.6263 libcairo.so.2.10800.8\r\n 91 0.5588 libpng12.so.0.49.0\r\n 88 0.5404 libfontconfig.so.1.4.4\r\n 79 0.4851 libdbi.so.0.0.5\r\n 58 0.3562 ld-2.12.so<\/pre>\n<p>libmysqlclient is the key here. But we need more information. Let&rsquo;s print symbols with opreport (note : you would need not stripped libraries for this, and it can be quite a pain to find \/ generate).<\/p>\n<pre>samples % image name symbol name\r\n14447 88.8281 libmysqlclient.so.16.0.0 mysql_data_seek\r\n53 \u00a0 \u00a0 \u00a0 \u00a00.3259 \u00a0libmysqlclient.so.16.0.0 _db_enter_\r\n33 \u00a0 \u00a0 \u00a0 \u00a00.2029 \u00a0libmysqlclient.so.16.0.0 _db_return_<\/pre>\n<p>so much time for data seek &#8230; I&rsquo;m sure we can do better, but it would need some specific skills. I&rsquo;ll report it to the rrd-developers mailing list. Maybe someone may look into it.<\/p>\n<p><strong>UPDATE 2013-01-09<\/strong><br \/>\nProblem is located in libdbi and implementation of\u00a0dbi_result_seek_row(), that is using mysql_data_seek() everytime.<\/p>\n<p>A patch was commited on CVS version of libdbi and libdbi-drivers today, and I tested it successfully. Here are performance with the patch :<\/p>\n<p><a href=\"https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/benchmark_with_patch.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-83\" alt=\"RRD graph with libdbi patch\" src=\"https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/benchmark_with_patch-300x191.png\" width=\"300\" height=\"191\" srcset=\"https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/benchmark_with_patch-300x191.png 300w, https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/benchmark_with_patch.png 674w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>or if you prefer, difference before and after the fix in libdbi :<\/p>\n<p><a href=\"https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/bugfixlibdbi.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-86\" alt=\"perf with libdbi patch\" src=\"https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/bugfixlibdbi-300x191.png\" width=\"300\" height=\"191\" srcset=\"https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/bugfixlibdbi-300x191.png 300w, https:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/bugfixlibdbi.png 674w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Performances seems to be linear and are quite acceptable now. For your information, here is what take time when asking for 200 hours of data with 5DS :<\/p>\n<table style=\"width: 100%;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<thead>\n<tr>\n<td>Action<\/td>\n<td>Total time in ms<\/td>\n<td><\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>SQL Queries<\/td>\n<td align=\"right\">60<\/td>\n<td>5 queries (one for each DS)<\/td>\n<\/tr>\n<tr>\n<td>Data retrieving \/ transformation<\/td>\n<td align=\"right\">100<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td>Others<\/td>\n<td align=\"right\">80<\/td>\n<td><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>I think I&rsquo;ll patch rrdtool to add some benchmark timers to see if there is still place for optimizations (especially this data retrieving \/ transformation part).<\/p>\n<p>I hope libdbi team will provide an official release soon. I&rsquo;ll update this blog post \u00a0with instructions to have latest libdbi at that time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A few months ago, I started some tests with RRDTool and a data source in MySQL. At that time, I just saw that performance was awful but did not investigate further. Now is time to check why, and if any improvements could be made.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-74","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>RRDTool with MySQL - My thoughts<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"RRDTool with MySQL - My thoughts\" \/>\n<meta property=\"og:description\" content=\"A few months ago, I started some tests with RRDTool and a data source in MySQL. At that time, I just saw that performance was awful but did not investigate further. Now is time to check why, and if any improvements could be made.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/\" \/>\n<meta property=\"og:site_name\" content=\"My thoughts\" \/>\n<meta property=\"article:published_time\" content=\"2012-12-31T17:57:41+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2013-01-09T15:43:27+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/graph-300x202.png\" \/>\n<meta name=\"author\" content=\"Olivier\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@odoucet\" \/>\n<meta name=\"twitter:site\" content=\"@odoucet\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"Olivier\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/\",\"url\":\"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/\",\"name\":\"RRDTool with MySQL - My thoughts\",\"isPartOf\":{\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/#primaryimage\"},\"thumbnailUrl\":\"http:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/graph-300x202.png\",\"datePublished\":\"2012-12-31T17:57:41+00:00\",\"dateModified\":\"2013-01-09T15:43:27+00:00\",\"author\":{\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/#\/schema\/person\/d093cada9eddc4839cbae3de5c823a39\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/#primaryimage\",\"url\":\"http:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/graph-300x202.png\",\"contentUrl\":\"http:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/graph-300x202.png\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.olivierdoucet.info\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"RRDTool with MySQL\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/#website\",\"url\":\"https:\/\/www.olivierdoucet.info\/blog\/\",\"name\":\"My thoughts\",\"description\":\"En fran\u00e7ais and in english :)\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.olivierdoucet.info\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/#\/schema\/person\/d093cada9eddc4839cbae3de5c823a39\",\"name\":\"Olivier\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/www.olivierdoucet.info\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e75f77ace92ccb2dfab09f226ce3d329?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e75f77ace92ccb2dfab09f226ce3d329?s=96&d=mm&r=g\",\"caption\":\"Olivier\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"RRDTool with MySQL - My thoughts","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/","og_locale":"fr_FR","og_type":"article","og_title":"RRDTool with MySQL - My thoughts","og_description":"A few months ago, I started some tests with RRDTool and a data source in MySQL. At that time, I just saw that performance was awful but did not investigate further. Now is time to check why, and if any improvements could be made.","og_url":"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/","og_site_name":"My thoughts","article_published_time":"2012-12-31T17:57:41+00:00","article_modified_time":"2013-01-09T15:43:27+00:00","og_image":[{"url":"http:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/graph-300x202.png","type":"","width":"","height":""}],"author":"Olivier","twitter_card":"summary_large_image","twitter_creator":"@odoucet","twitter_site":"@odoucet","twitter_misc":{"\u00c9crit par":"Olivier","Dur\u00e9e de lecture estim\u00e9e":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/","url":"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/","name":"RRDTool with MySQL - My thoughts","isPartOf":{"@id":"https:\/\/www.olivierdoucet.info\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/#primaryimage"},"image":{"@id":"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/#primaryimage"},"thumbnailUrl":"http:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/graph-300x202.png","datePublished":"2012-12-31T17:57:41+00:00","dateModified":"2013-01-09T15:43:27+00:00","author":{"@id":"https:\/\/www.olivierdoucet.info\/blog\/#\/schema\/person\/d093cada9eddc4839cbae3de5c823a39"},"breadcrumb":{"@id":"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/"]}]},{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/#primaryimage","url":"http:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/graph-300x202.png","contentUrl":"http:\/\/www.olivierdoucet.info\/blog\/wp-content\/uploads\/2012\/12\/graph-300x202.png"},{"@type":"BreadcrumbList","@id":"https:\/\/www.olivierdoucet.info\/blog\/2012\/12\/31\/rrdtool-mysql\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.olivierdoucet.info\/blog\/"},{"@type":"ListItem","position":2,"name":"RRDTool with MySQL"}]},{"@type":"WebSite","@id":"https:\/\/www.olivierdoucet.info\/blog\/#website","url":"https:\/\/www.olivierdoucet.info\/blog\/","name":"My thoughts","description":"En fran\u00e7ais and in english :)","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.olivierdoucet.info\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":"Person","@id":"https:\/\/www.olivierdoucet.info\/blog\/#\/schema\/person\/d093cada9eddc4839cbae3de5c823a39","name":"Olivier","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/www.olivierdoucet.info\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e75f77ace92ccb2dfab09f226ce3d329?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e75f77ace92ccb2dfab09f226ce3d329?s=96&d=mm&r=g","caption":"Olivier"}}]}},"_links":{"self":[{"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/posts\/74","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/comments?post=74"}],"version-history":[{"count":9,"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/posts\/74\/revisions"}],"predecessor-version":[{"id":82,"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/posts\/74\/revisions\/82"}],"wp:attachment":[{"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/media?parent=74"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/categories?post=74"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.olivierdoucet.info\/blog\/wp-json\/wp\/v2\/tags?post=74"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}