Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]

   1  <?php
   2  /**
   3   * Library for basic performance monitoring and tuning
   4   *
   5   * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
   6   *
   7   * @package ADOdb
   8   * @link https://adodb.org Project's web site and documentation
   9   * @link https://github.com/ADOdb/ADOdb Source code and issue tracker
  10   *
  11   * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause
  12   * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option,
  13   * any later version. This means you can use it in proprietary products.
  14   * See the LICENSE.md file distributed with this source code for details.
  15   * @license BSD-3-Clause
  16   * @license LGPL-2.1-or-later
  17   *
  18   * @copyright 2000-2013 John Lim
  19   * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
  20   */
  21  
  22  // security - hide paths
  23  if (!defined('ADODB_DIR')) die();
  24  
  25  class perf_mysql extends adodb_perf{
  26  
  27  	 var $tablesSQL = 'show table status';
  28  
  29  	 var $createTableSQL = "CREATE TABLE adodb_logsql (
  30  	 	   created datetime NOT NULL,
  31  	 	   sql0 varchar(250) NOT NULL,
  32  	 	   sql1 text NOT NULL,
  33  	 	   params text NOT NULL,
  34  	 	   tracer text NOT NULL,
  35  	 	   timer decimal(16,6) NOT NULL
  36  	 	 )";
  37  
  38  	 var $settings = array(
  39  	 'Ratios',
  40  	 	 'MyISAM cache hit ratio' => array('RATIO',
  41  	 	 	 '=GetKeyHitRatio',
  42  	 	 	 '=WarnCacheRatio'),
  43  	 	 'InnoDB cache hit ratio' => array('RATIO',
  44  	 	 	 '=GetInnoDBHitRatio',
  45  	 	 	 '=WarnCacheRatio'),
  46  	 	 'data cache hit ratio' => array('HIDE', # only if called
  47  	 	 	 '=FindDBHitRatio',
  48  	 	 	 '=WarnCacheRatio'),
  49  	 	 'sql cache hit ratio' => array('RATIO',
  50  	 	 	 '=GetQHitRatio',
  51  	 	 	 ''),
  52  	 'IO',
  53  	 	 'data reads' => array('IO',
  54  	 	 	 '=GetReads',
  55  	 	 	 'Number of selects (Key_reads is not accurate)'),
  56  	 	 'data writes' => array('IO',
  57  	 	 	 '=GetWrites',
  58  	 	 	 'Number of inserts/updates/deletes * coef (Key_writes is not accurate)'),
  59  
  60  	 'Data Cache',
  61  	 	 'MyISAM data cache size' => array('DATAC',
  62  	 	 	 array("show variables", 'key_buffer_size'),
  63  	 	 	 '' ),
  64  	 	 'BDB data cache size' => array('DATAC',
  65  	 	 	 array("show variables", 'bdb_cache_size'),
  66  	 	 	 '' ),
  67  	 	 'InnoDB data cache size' => array('DATAC',
  68  	 	 	 array("show variables", 'innodb_buffer_pool_size'),
  69  	 	 	 '' ),
  70  	 'Memory Usage',
  71  	 	 'read buffer size' => array('CACHE',
  72  	 	 	 array("show variables", 'read_buffer_size'),
  73  	 	 	 '(per session)'),
  74  	 	 'sort buffer size' => array('CACHE',
  75  	 	 	 array("show variables", 'sort_buffer_size'),
  76  	 	 	 'Size of sort buffer (per session)' ),
  77  	 	 'table cache' => array('CACHE',
  78  	 	 	 array("show variables", 'table_cache'),
  79  	 	 	 'Number of tables to keep open'),
  80  	 'Connections',
  81  	 	 'current connections' => array('SESS',
  82  	 	 	 array('show status','Threads_connected'),
  83  	 	 	 ''),
  84  	 	 'max connections' => array( 'SESS',
  85  	 	 	 array("show variables",'max_connections'),
  86  	 	 	 ''),
  87  
  88  	 	 false
  89  	 );
  90  
  91  	function __construct(&$conn)
  92  	 {
  93  	 	 $this->conn = $conn;
  94  	 }
  95  
  96  	function Explain($sql,$partial=false)
  97  	 {
  98  
  99  	 	 if (strtoupper(substr(trim($sql),0,6)) !== 'SELECT') return '<p>Unable to EXPLAIN non-select statement</p>';
 100  	 	 $save = $this->conn->LogSQL(false);
 101  	 	 if ($partial) {
 102  	 	 	 $sqlq = $this->conn->qstr($sql.'%');
 103  	 	 	 $arr = $this->conn->GetArray("select distinct sql1 from adodb_logsql where sql1 like $sqlq");
 104  	 	 	 if ($arr) {
 105  	 	 	 	 foreach($arr as $row) {
 106  	 	 	 	 	 $sql = reset($row);
 107  	 	 	 	 	 if (crc32($sql) == $partial) break;
 108  	 	 	 	 }
 109  	 	 	 }
 110  	 	 }
 111  	 	 $sql = str_replace('?',"''",$sql);
 112  
 113  	 	 if ($partial) {
 114  	 	 	 $sqlq = $this->conn->qstr($sql.'%');
 115  	 	 	 $sql = $this->conn->GetOne("select sql1 from adodb_logsql where sql1 like $sqlq");
 116  	 	 }
 117  
 118  	 	 $s = '<p><b>Explain</b>: '.htmlspecialchars($sql).'</p>';
 119  	 	 $rs = $this->conn->Execute('EXPLAIN '.$sql);
 120  	 	 $s .= rs2html($rs,false,false,false,false);
 121  	 	 $this->conn->LogSQL($save);
 122  	 	 $s .= $this->Tracer($sql);
 123  	 	 return $s;
 124  	 }
 125  
 126  	 /**
 127  	  * Returns a list of table statuses.
 128  	  *
 129  	  * @param  string  $orderby  Unused (compatibility with parent method)
 130  	  * @return string A formatted set of recordsets
 131  	  */
 132  	function tables($orderby='1')
 133  	 {
 134  	 	 if (!$this->tablesSQL) return false;
 135  
 136  	 	 $rs = $this->conn->Execute($this->tablesSQL);
 137  	 	 if (!$rs) return false;
 138  
 139  	 	 $html = rs2html($rs,false,false,false,false);
 140  	 	 return $html;
 141  	 }
 142  
 143  	function GetReads()
 144  	 {
 145  	 global $ADODB_FETCH_MODE;
 146  	 	 $save = $ADODB_FETCH_MODE;
 147  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 148  	 	 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
 149  
 150  	 	 $rs = $this->conn->Execute('show status');
 151  
 152  	 	 if (isset($savem)) $this->conn->SetFetchMode($savem);
 153  	 	 $ADODB_FETCH_MODE = $save;
 154  
 155  	 	 if (!$rs) return 0;
 156  	 	 $val = 0;
 157  	 	 while (!$rs->EOF) {
 158  	 	 	 switch($rs->fields[0]) {
 159  	 	 	 case 'Com_select':
 160  	 	 	 	 $val = $rs->fields[1];
 161  	 	 	 	 $rs->Close();
 162  	 	 	 	 return $val;
 163  	 	 	 }
 164  	 	 	 $rs->MoveNext();
 165  	 	 }
 166  
 167  	 	 $rs->Close();
 168  
 169  	 	 return $val;
 170  	 }
 171  
 172  	function GetWrites()
 173  	 {
 174  	 global $ADODB_FETCH_MODE;
 175  	 	 $save = $ADODB_FETCH_MODE;
 176  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 177  	 	 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
 178  
 179  	 	 $rs = $this->conn->Execute('show status');
 180  
 181  	 	 if (isset($savem)) $this->conn->SetFetchMode($savem);
 182  	 	 $ADODB_FETCH_MODE = $save;
 183  
 184  	 	 if (!$rs) return 0;
 185  	 	 $val = 0.0;
 186  	 	 while (!$rs->EOF) {
 187  	 	 	 switch($rs->fields[0]) {
 188  	 	 	 case 'Com_insert':
 189  	 	 	 	 $val += $rs->fields[1]; break;
 190  	 	 	 case 'Com_delete':
 191  	 	 	 	 $val += $rs->fields[1]; break;
 192  	 	 	 case 'Com_update':
 193  	 	 	 	 $val += $rs->fields[1]/2;
 194  	 	 	 	 $rs->Close();
 195  	 	 	 	 return $val;
 196  	 	 	 }
 197  	 	 	 $rs->MoveNext();
 198  	 	 }
 199  
 200  	 	 $rs->Close();
 201  
 202  	 	 return $val;
 203  	 }
 204  
 205  	function FindDBHitRatio()
 206  	 {
 207  	 	 // first find out type of table
 208  	 	 //$this->conn->debug=1;
 209  
 210  	 	 global $ADODB_FETCH_MODE;
 211  	 	 $save = $ADODB_FETCH_MODE;
 212  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 213  	 	 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
 214  
 215  	 	 $rs = $this->conn->Execute('show table status');
 216  
 217  	 	 if (isset($savem)) $this->conn->SetFetchMode($savem);
 218  	 	 $ADODB_FETCH_MODE = $save;
 219  
 220  	 	 if (!$rs) return '';
 221  	 	 $type = strtoupper($rs->fields[1]);
 222  	 	 $rs->Close();
 223  	 	 switch($type){
 224  	 	 case 'MYISAM':
 225  	 	 case 'ISAM':
 226  	 	 	 return $this->DBParameter('MyISAM cache hit ratio').' (MyISAM)';
 227  	 	 case 'INNODB':
 228  	 	 	 return $this->DBParameter('InnoDB cache hit ratio').' (InnoDB)';
 229  	 	 default:
 230  	 	 	 return $type.' not supported';
 231  	 	 }
 232  
 233  	 }
 234  
 235  	function GetQHitRatio()
 236  	 {
 237  	 	 //Total number of queries = Qcache_inserts + Qcache_hits + Qcache_not_cached
 238  	 	 $hits = $this->_DBParameter(array("show status","Qcache_hits"));
 239  	 	 $total = $this->_DBParameter(array("show status","Qcache_inserts"));
 240  	 	 $total += $this->_DBParameter(array("show status","Qcache_not_cached"));
 241  
 242  	 	 $total += $hits;
 243  	 	 if ($total) return round(($hits*100)/$total,2);
 244  	 	 return 0;
 245  	 }
 246  
 247  	 /*
 248  	 	 Use session variable to store Hit percentage, because MySQL
 249  	 	 does not remember last value of SHOW INNODB STATUS hit ratio
 250  
 251  	 	 # 1st query to SHOW INNODB STATUS
 252  	 	 0.00 reads/s, 0.00 creates/s, 0.00 writes/s
 253  	 	 Buffer pool hit rate 1000 / 1000
 254  
 255  	 	 # 2nd query to SHOW INNODB STATUS
 256  	 	 0.00 reads/s, 0.00 creates/s, 0.00 writes/s
 257  	 	 No buffer pool activity since the last printout
 258  	 */
 259  	function GetInnoDBHitRatio()
 260  	 {
 261  	 global $ADODB_FETCH_MODE;
 262  
 263  	 	 $save = $ADODB_FETCH_MODE;
 264  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 265  	 	 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
 266  
 267  	 	 $rs = $this->conn->Execute('show engine innodb status');
 268  
 269  	 	 if (isset($savem)) $this->conn->SetFetchMode($savem);
 270  	 	 $ADODB_FETCH_MODE = $save;
 271  
 272  	 	 if (!$rs || $rs->EOF) return 0;
 273  	 	 $stat = $rs->fields[0];
 274  	 	 $rs->Close();
 275  	 	 $at = strpos($stat,'Buffer pool hit rate');
 276  	 	 $stat = substr($stat,$at,200);
 277  	 	 if (preg_match('!Buffer pool hit rate\s*([0-9]*) / ([0-9]*)!',$stat,$arr)) {
 278  	 	 	 $val = 100*$arr[1]/$arr[2];
 279  	 	 	 $_SESSION['INNODB_HIT_PCT'] = $val;
 280  	 	 	 return round($val,2);
 281  	 	 } else {
 282  	 	 	 if (isset($_SESSION['INNODB_HIT_PCT'])) return $_SESSION['INNODB_HIT_PCT'];
 283  	 	 	 return 0;
 284  	 	 }
 285  	 	 return 0;
 286  	 }
 287  
 288  	function GetKeyHitRatio()
 289  	 {
 290  	 	 $hits = $this->_DBParameter(array("show status","Key_read_requests"));
 291  	 	 $reqs = $this->_DBParameter(array("show status","Key_reads"));
 292  	 	 if ($reqs == 0) return 0;
 293  
 294  	 	 return round(($hits/($reqs+$hits))*100,2);
 295  	 }
 296  
 297      // start hack
 298      var $optimizeTableLow = 'CHECK TABLE %s FAST QUICK';
 299      var $optimizeTableHigh = 'OPTIMIZE TABLE %s';
 300  
 301      /**
 302       * @see adodb_perf::optimizeTable()
 303       */
 304       function optimizeTable( $table, $mode = ADODB_OPT_LOW)
 305       {
 306          if ( !is_string( $table)) return false;
 307  
 308          $conn = $this->conn;
 309          if ( !$conn) return false;
 310  
 311          $sql = '';
 312          switch( $mode) {
 313              case ADODB_OPT_LOW : $sql = $this->optimizeTableLow; break;
 314              case ADODB_OPT_HIGH : $sql = $this->optimizeTableHigh; break;
 315              default :
 316                  // May don't use __FUNCTION__ constant for BC (__FUNCTION__ Added in PHP 4.3.0)
 317                  ADOConnection::outp( sprintf( "<p>%s: '%s' using of undefined mode '%s'</p>", __CLASS__, __FUNCTION__, $mode));
 318                  return false;
 319          }
 320          $sql = sprintf( $sql, $table);
 321  
 322          return $conn->Execute( $sql) !== false;
 323       }
 324      // end hack
 325  }