Does anyone know how Drupal.org (D6) splits their MySQL read and write queries to a slave and master database? I am trying to get all "read" queries go to a slave database and all "write" queries go to a master database.
I was told that there isn't a safe way to split all SELECT queries to go to a slave (read-only) because not all SELECT queries are slave-safe. But, Drupal.org does it successfully so I would greatly appreciate if someone could give me information on this.
I tried this patch by SwampCritter, but I couldn't get it to work: http://msrwd.svn.sourceforge.net/viewvc/msrwd/D6/
Whenever I try to create new content, I get an error message: "Post could not be saved" and the post is not saved.
I have also tried to modify /includes/database.mysql-common.inc on my own in order to split the read/write queries to go to a slave/master db:
function db_query($query) {
$args = func_get_args();
array_shift($args);
$query = db_prefix_tables($query);
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
$args = $args[0];
}
_db_query_callback($args, TRUE);
$query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
/* Added by apersaud
Integrating multiple read/write databases
*/
if(strpos(strtolower($query),"select") === 0){
db_set_active('readonly'); //this will not contain any data from the master (write) database untill replication happens
}
else {
db_set_active('default');
}
return _db_query($query);
db_set_active('default');
}
And here's how I set up the read/write datbases in settings.php:
$db_url['default'] = 'mysql://username:password@localhost/databasename';
$db_url['readonly'] = 'mysql://username:password@localhost/databasename';
However, this also didn't work. Again, when I try to create new content, I get an error message "Post could not be saved" and also the only row of data in the node_access table is deleted upon this action (which causes access permissions problems for users). Also, in the log entries I get these errors:
Duplicate entry '0' for key 2 query: INSERT INTO node (vid, type, language, title, uid, status, created, changed, comment, promote, moderate, sticky, tnid, translate) VALUES (0, 'story', '', 'Test 9', 6, 0, 1242671928, 1242671928, 0, 0, 0, 0, 0, 0) in .../includes/common.inc on line 3422.
Does anyone know what patch Drupal.org applied in order to achieve this functionality successfully in D6?
Comments
Got it working!
I found out drupal.org uses a patch developed by "Four Kitchens" in order to achieve this functionality, so I don't know how they did it.
However, I was able to hack something together that seems to be working so far on Drupal 6. I modified one file '/includes/database.mysql-common.inc':
It's not the prettiest solution, but it works. I did it this way because not all SELECT read queries are slave-safe, so I tried to catch those non-slave-safe queries and direct them to the master database. I might catch some SELECT queries that should probably go to the slave but I don't think there's any harm in it going to the master instead.
Direct all LAST_INSERT_ID() to master database
Directing queries containing LAST_INSERT_ID() function to the slave returns incorrect results.
Adding one more conidtion:
if(strpos(strtolower($query),"LAST_INSERT_ID()") === FALSE){
....
....
}
else{
db_set_active('default');
}
I combine the both, so the
I combine the both, so the final function is like this
It works well, thank you all.
sub
subscribing
We're using Pressflow since
We're using Pressflow since it has support for slave databases. But, the slave databases are severely under utilized since db_query_slave/db_query_range_slave/etc have to be used to send the query to the slaves. Unless you want to patch every module to use db_query_slave instead of db_query and so on, no more than 10% of query will ever go to the slaves.
So, what we did is patch the include file, 'database.mysqli.inc', specifically the function '_db_query()'.
The patch below basically overwrites the parameter $slave (if false) by checking the query if it is slave save by doing some logic:
The list of slave save tables is defined in the settings.php. For example:
Basically, we chose tables that are fairly 'static' in nature. Also, "*" is the wild card, so that blocks and blocks_roles tables are included.
Here is the patch:
The _db_query_is_slave_safe got a problem
I found a problem on
if (strpos($query,"select ") !== false && !preg_match("/alter|insert|update|drop|delete|flush|lock|create|last_insert_id|found_rows|sql_calc_found_rows|row_count/", $query)) {If I send query llike this
SELECT node.title, node.created FROM nodeIt will return false, because the syntax have "create" on "node.created".
So I changed the code to
But I don't know it safe? or there have better solution?
marked
marked
Got couple of issues with this patch
Got couple of issues with this patch listed below:
1) When the "Devel" Module is on it is not working as expected.
> Devel add username and source function name before each query like
$query = '/* '. $name .' : '. $bt[2]['function'] .' */ '. $query;
As a result function having the keywords (i.e *lock*) will go in Master DB instead of Read replica.
> If there is any issue in MySQL query, problem in mysql_error and trigger_error.
2) When you have more read only tables (in my case having more than 100 read tables), there might be performance issue. As a solution check master must tables and rest of the query sent to Read only DB.
3) For heavy transaction site MySQL replication process itself would takes around 2-3 sec. That is why in immediate read ((any data retrieval attempt before replication of data from Master to Slave DB) page SQLs will need to be executed from Master DB. For example profile edit page.
Taking care of all these issue, updated patches are listed below: