r91442 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r91441‎ | r91442 | r91443 >
Date:07:04, 5 July 2011
Author:tstarling
Status:ok
Tags:
Comment:
MFT r91402: work around excessive locking in InnoDB by "sharding" across multiple tables.
Modified paths:
  • /branches/wmf/1.17wmf1/includes/objectcache/SqlBagOStuff.php (modified) (history)

Diff [purge]

Index: branches/wmf/1.17wmf1/includes/objectcache/SqlBagOStuff.php
@@ -9,6 +9,8 @@
1010 var $lb, $db, $serverInfo;
1111 var $lastExpireAll = 0;
1212 var $purgePeriod = 100;
 13+ var $shards = 1;
 14+ var $tableName = 'objectcache';
1315
1416 /**
1517 * Constructor. Parameters are:
@@ -21,6 +23,18 @@
2224 * reciprocal of the probability of purging on any given
2325 * request. If this is set to zero, purging will never be
2426 * done.
 27+ *
 28+ * - tableName: The table name to use, default is "objectcache".
 29+ *
 30+ * - shards: The number of tables to use for data storage. If this is
 31+ * more than 1, table names will be formed in the style
 32+ * objectcacheNNN where NNN is the shard index, between 0 and
 33+ * shards-1. The number of digits will be the minimum number
 34+ * required to hold the largest shard index. Data will be
 35+ * distributed across all tables by key hash. This is for
 36+ * MySQL bugs 61735 and 61736.
 37+ *
 38+ * @param $params array
2539 */
2640 public function __construct( $params ) {
2741 if ( isset( $params['server'] ) ) {
@@ -30,6 +44,12 @@
3145 if ( isset( $params['purgePeriod'] ) ) {
3246 $this->purgePeriod = intval( $params['purgePeriod'] );
3347 }
 48+ if ( isset( $params['tableName'] ) ) {
 49+ $this->tableName = $params['tableName'];
 50+ }
 51+ if ( isset( $params['shards'] ) ) {
 52+ $this->shards = intval( $params['shards'] );
 53+ }
3454 }
3555
3656 protected function getDB() {
@@ -57,11 +77,37 @@
5878 return $this->db;
5979 }
6080
 81+ /**
 82+ * Get the table name for a given key
 83+ */
 84+ protected function getTableByKey( $key ) {
 85+ if ( $this->shards > 1 ) {
 86+ $hash = hexdec( substr( md5( $key ), 0, 8 ) ) & 0x7fffffff;
 87+ return $this->getTableByShard( $hash % $this->shards );
 88+ } else {
 89+ return $this->tableName;
 90+ }
 91+ }
 92+
 93+ /**
 94+ * Get the table name for a given shard index
 95+ */
 96+ protected function getTableByShard( $index ) {
 97+ if ( $this->shards > 1 ) {
 98+ $decimals = strlen( $this->shards - 1 );
 99+ return $this->tableName .
 100+ sprintf( "%0{$decimals}d", $index );
 101+ } else {
 102+ return $this->tableName;
 103+ }
 104+ }
 105+
61106 public function get( $key ) {
62107 # expire old entries if any
63108 $this->garbageCollect();
64109 $db = $this->getDB();
65 - $row = $db->selectRow( 'objectcache', array( 'value', 'exptime' ),
 110+ $tableName = $this->getTableByKey( $key );
 111+ $row = $db->selectRow( $tableName, array( 'value', 'exptime' ),
66112 array( 'keyname' => $key ), __METHOD__ );
67113
68114 if ( !$row ) {
@@ -77,7 +123,7 @@
78124 $db->begin();
79125 # Put the expiry time in the WHERE condition to avoid deleting a
80126 # newly-inserted value
81 - $db->delete( 'objectcache',
 127+ $db->delete( $tableName,
82128 array(
83129 'keyname' => $key,
84130 'exptime' => $row->exptime
@@ -114,7 +160,9 @@
115161 $db->begin();
116162 // (bug 24425) use a replace if the db supports it instead of
117163 // delete/insert to avoid clashes with conflicting keynames
118 - $db->replace( 'objectcache', array( 'keyname' ),
 164+ $db->replace(
 165+ $this->getTableByKey( $key ),
 166+ array( 'keyname' ),
119167 array(
120168 'keyname' => $key,
121169 'value' => $db->encodeBlob( $this->serialize( $value ) ),
@@ -135,7 +183,10 @@
136184
137185 try {
138186 $db->begin();
139 - $db->delete( 'objectcache', array( 'keyname' => $key ), __METHOD__ );
 187+ $db->delete(
 188+ $this->getTableByKey( $key ),
 189+ array( 'keyname' => $key ),
 190+ __METHOD__ );
140191 $db->commit();
141192 } catch ( DBQueryError $e ) {
142193 $this->handleWriteError( $e );
@@ -148,19 +199,24 @@
149200
150201 public function incr( $key, $step = 1 ) {
151202 $db = $this->getDB();
 203+ $tableName = $this->getTableByKey( $key );
152204 $step = intval( $step );
153205
154206 try {
155207 $db->begin();
156 - $row = $db->selectRow( 'objectcache', array( 'value', 'exptime' ),
157 - array( 'keyname' => $key ), __METHOD__, array( 'FOR UPDATE' ) );
 208+ $row = $db->selectRow(
 209+ $tableName,
 210+ array( 'value', 'exptime' ),
 211+ array( 'keyname' => $key ),
 212+ __METHOD__,
 213+ array( 'FOR UPDATE' ) );
158214 if ( $row === false ) {
159215 // Missing
160216 $db->commit();
161217
162218 return null;
163219 }
164 - $db->delete( 'objectcache', array( 'keyname' => $key ), __METHOD__ );
 220+ $db->delete( $tableName, array( 'keyname' => $key ), __METHOD__ );
165221 if ( $this->isExpired( $row->exptime ) ) {
166222 // Expired, do not reinsert
167223 $db->commit();
@@ -170,7 +226,7 @@
171227
172228 $oldValue = intval( $this->unserialize( $db->decodeBlob( $row->value ) ) );
173229 $newValue = $oldValue + $step;
174 - $db->insert( 'objectcache',
 230+ $db->insert( $tableName,
175231 array(
176232 'keyname' => $key,
177233 'value' => $db->encodeBlob( $this->serialize( $newValue ) ),
@@ -188,11 +244,14 @@
189245
190246 public function keys() {
191247 $db = $this->getDB();
192 - $res = $db->select( 'objectcache', array( 'keyname' ), false, __METHOD__ );
193248 $result = array();
194249
195 - foreach ( $res as $row ) {
196 - $result[] = $row->keyname;
 250+ for ( $i = 0; $i < $this->shards; $i++ ) {
 251+ $res = $db->select( $this->getTableByShard( $i ),
 252+ array( 'keyname' ), false, __METHOD__ );
 253+ foreach ( $res as $row ) {
 254+ $result[] = $row->keyname;
 255+ }
197256 }
198257
199258 return $result;
@@ -232,9 +291,14 @@
233292 $now = $db->timestamp();
234293
235294 try {
236 - $db->begin();
237 - $db->delete( 'objectcache', array( 'exptime < ' . $db->addQuotes( $now ) ), __METHOD__ );
238 - $db->commit();
 295+ for ( $i = 0; $i < $this->shards; $i++ ) {
 296+ $db->begin();
 297+ $db->delete(
 298+ $this->getTableByShard( $i ),
 299+ array( 'exptime < ' . $db->addQuotes( $now ) ),
 300+ __METHOD__ );
 301+ $db->commit();
 302+ }
239303 } catch ( DBQueryError $e ) {
240304 $this->handleWriteError( $e );
241305 }
@@ -244,9 +308,11 @@
245309 $db = $this->getDB();
246310
247311 try {
248 - $db->begin();
249 - $db->delete( 'objectcache', '*', __METHOD__ );
250 - $db->commit();
 312+ for ( $i = 0; $i < $this->shards; $i++ ) {
 313+ $db->begin();
 314+ $db->delete( $this->getTableByShard( $i ), '*', __METHOD__ );
 315+ $db->commit();
 316+ }
251317 } catch ( DBQueryError $e ) {
252318 $this->handleWriteError( $e );
253319 }
@@ -308,6 +374,27 @@
309375 wfDebug( __METHOD__ . ": ignoring query error\n" );
310376 $db->ignoreErrors( false );
311377 }
 378+
 379+ /**
 380+ * Create shard tables. For use from eval.php.
 381+ */
 382+ public function createTables() {
 383+ $db = $this->getDB();
 384+ if ( $db->getType() !== 'mysql'
 385+ || version_compare( $db->getServerVersion(), '4.1.0', '<' ) )
 386+ {
 387+ throw new MWException( __METHOD__ . ' is not supported on this DB server' );
 388+ }
 389+
 390+ for ( $i = 0; $i < $this->shards; $i++ ) {
 391+ $db->begin();
 392+ $db->query(
 393+ 'CREATE TABLE ' . $db->tableName( $this->getTableByShard( $i ) ) .
 394+ ' LIKE ' . $db->tableName( 'objectcache' ),
 395+ __METHOD__ );
 396+ $db->commit();
 397+ }
 398+ }
312399 }
313400
314401 /**
Property changes on: branches/wmf/1.17wmf1/includes/objectcache/SqlBagOStuff.php
___________________________________________________________________
Modified: svn:mergeinfo
315402 Merged /trunk/phase3/includes/objectcache/SqlBagOStuff.php:r91402

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r91402Allow SqlBagOStuff data to be split over many tables, to avoid lock contentio...tstarling12:01, 4 July 2011

Status & tagging log