Monday, March 18, 2013

Rules of Thumb for MySQL


SELECTs -- do's and don'ts

RoTsDiscussion

    ⚈  Do not hide an indexed column inside a function call: DATE(x) = '...', LCASE(col) = 'foo'

    ⚈  LCASE() is usually unnecessary because the collation will compare 'correctly' without it.

    ⚈  #1: Subqueries perform poorly

    ⚈  Never use "IN (SELECT...)" -- optimizes poorly. Turn into JOIN. (5.6.5 improves)

    ⚈  A subquery that condenses data (GROUP BY, LIMIT, etc) may perform well

    ⚈  OR may be very inefficient; turn into UNION.

    ⚈  A coding pattern:
dt >= '2010-02-01' AND dt < '2010-02-01' + INTERVAL 7 DAY

    ⚈  ORDER BY NULL -- a little-known trick to avoid GROUP BY doing a sort (if there is another way).

    ⚈  WHERE (a,b) > (7,8) is poorly optimized

    ⚈  Gather these to study a slow query: SHOW CREATE TABLE, SHOW TABLE STATUS, EXPLAIN.

    ⚈  Do not use OFFSET for pagination -- continue where you "left off"

    ⚈  Don't mix DISTINCT and GROUP BY

    ⚈  Be explicit about UNION ALL vs UNION DISTINCT -- it makes you think about which to use

    ⚈  Do not use SELECT * except for debugging or when fetching into a hash.

Subqueries came to MySQL rather late in the game. They have not been well optimized, so it is usually better to turn your SELECTs into an equivalent JOIN. This is especially true for "IN ( SELECT ... )", but that will be well optimized soon (5.6.5 and MariaDB 5.5).

Sometimes a subquery is really the best way to optimize a SELECT. The common thread of these "good" subqueries seems to be when the subquery has to scan a lot of rows, but boils down the intermediate resultset to a small number of rows. This is likely to happen with GROUP BY or LIMIT in the subquery.

Index hints (FORCE INDEX, etc) may help you today, but may be the wrong thing for tomorrow -- different constants in the WHERE clause may lead FORCE to do the "wrong" thing.

For analyzing a slow query, SHOW CREATE TABLE provides the datatypes, indexes, and engine. (DESCRIBE provides much less info.) SHOW TABLE STATUS tells how big the table is. EXPLAIN says how the query optimizer decided to perform the query.

It is so tempting to use ORDER BY id LIMIT 30,10 to find the 4th page of 10 items. But it is so inefficient, especially when you have thousands of pages. The thousandth page has to read (at some level) all the pages before it. "Left off" refers to having the "Next" button on one page give the id (or other sequencing info) of where the next page can be found. Then that page simply does WHERE id > $leftoff ORDER BY id LIMIT 10. More on pagination.



INDEXing

RoTsDiscussion

    ⚈  #1: Start an INDEX with "="s from the WHERE clause, then one other thing (range, group, order)

    ⚈  Terms: PRIMARY KEY > UNIQUE > INDEX = KEY

    ⚈  An index _may_ speed up a SELECT by orders of magnitude and will slow down INSERTs a little. (A fair tradeoff?)

    ⚈  Adding indexes is not a panacea.

    ⚈  BTree is an excellent all-around indexing mechanism

    ⚈  A BTree index node contains ~100 items. (1M rows = 3 levels; 1B rows = 5 levels)

    ⚈  Flags, and other fields with few values, should not be alone in an index -- the index won't be used.

    ⚈  MySQL rarely uses two INDEXes in one SELECT. Main exceptions: subqueries, UNION.

    ⚈  A "prefix" index -- INDEX(name(10)) -- is rarely useful. Exception: TEXT

    ⚈  A UNIQUE "prefix" is probably wrong -- UNIQUE(name(10)) forces 10 chars to be unique.

    ⚈  It is ok to have Index_length > Data_length

    ⚈  5 fields in a compound index seems "too many"

    ⚈  Having no compound indexes is a clue that you do not understand their power. INDEX(a,b) may be much better than INDEX(a), INDEX(b)

    ⚈  INDEX(a,b) "covers" for INDEX(a), so drop the latter.

    ⚈  2x speedup when "Using index" (a "covering" index)

    ⚈  Akiban (3rd party) "groups" tables together, interleaved, to improve JOIN performance.

    ⚈  FULLTEXT -- watch out for ft_min_word_len=4, stopwords, and 50% rule

    ⚈  A FULLTEXT index will be used before any other index.

    ⚈  FULLTEXT -- consider Syphinx, Lucene, etc (3rd Party)
Indexing is very important to any database. Getting the "right" index can make a query run orders of magnitude faster. So, how to do that? Often "compound indexes" (multiple columns in a single INDEX(...)) are better than single-column indexes. A WHERE clause that has column=constant begs for an index that starts with that column. If the WHERE clause has multiple fields AND'd together, "="s should come first.

INDEXes are structured in BTrees. The "root" node (a block) of a BTree has pointers to child blocks. This goes as deep as necessary, but really not very deep (see the 100x RoT). MyISAM uses 1KB blocks; InnoDB uses 16KB blocks.

Each INDEX is its own BTree. A PRIMARY KEY is a UNIQUE key is an INDEX. INDEX and KEY are synonomous. In InnoDB, the data included in the BTree for the PRIMARY KEY. In MyISAM, the data is a separate file (.MYD).

A "covering" index is one where _all_ the fields needed in a SELECT are included in the INDEX.


ENGINE Differences

RoTsDiscussion

    ⚈  InnoDB is faster than MyISAM -- contradicts the 'old' wisdom; InnoDB has improved.

    ⚈  The performance difference, alone, is not enough to justify changing Engines.

    ⚈  2x-3x bigger disk footprint for InnoDB than MyISAM

    ⚈  When converting from MyISAM to InnoDB, do learn the differences in INDEXes, especially the PRIMARY KEY.

    ⚈  A secondary index in InnoDB inplicitly includes the PRIMARY KEY.

    ⚈  An InnoDB table should have an explicit PRIMARY KEY, even if it is an artificial AUTO_INCREMENT.

    ⚈  "Rows" and "Avg_row_length" (SHOW TABLE STATUS) may be off by a factor of 2 (either way) for InnoDB.

    ⚈  For MyISAM, don't OPTIMIZE TABLE unless there is more than 10% Data_free according to SHOW TABLE STATUS

    ⚈  For InnoDB, don't OPTIMIZE TABLE -- it is rarely of any use (and it turns into an ALTER)

    ⚈  Use InnoDB instead of MyISAM.

    ⚈  Use Xtradb instead of InnoDB.

    ⚈  Don't let a BEGIN...COMMIT last more than a few seconds.

    ⚈  Use BEGIN, not autocommit=0 -- it is less confusing.
MyISAM dates back to the '90s. It was designed to be simple and fast, while lacking important database features (ACID). InnoDB came along in the '00s. Most energy has gone into improving it. Today, InnoDB, (and Percona's Xtradb) are excellent engines. InnoDB recovers automatically after a crash.

MyISAM's simplicity leads to very little overhead on disk. InnoDB's transactional semantics, MVCC, ROLLBACK, undo, etc, lead to a lot of disk overhead.

MyISAM caches INDEX blocks in the "key_buffer". Data blocks are cached by the Operating system. InnoDB caches both data and indexes in the "buffer_pool". These lead to radically different tuning (see 70%).

With InnoDB, either use autocommit=1, making each operation into a "transaction", or use BEGIN (START) and COMMIT to explicity bracket each set of statement(s) that make up a transaction. InnoDB can prevent many kinds of "deadlocks", and simply stall one transaction until it is safe to continue. Some deadlocks are easily detected after starting transactions, and lead to aborting one of the competing transactions; this ROLLBACK must be monitored for by the application. Still other situations can lead to hanging until a timeout (cf innodb_lock_wait_timeout, which defaults to 50 seconds).

MyISAM to InnoDB conversion tips


Optimizations, and not

RoTsDiscussion

    ⚈  "Using Temporary" and "Filesort" are not the end of the world. And, often they are unavoidable.

    ⚈  Might even get 2 Filesorts: GROUP BY x ORDER BY y

    ⚈  Avoid using index hints (USE/FORCE/IGNORE/STRAIGHT_JOIN)

    ⚈  You cannot "tune your way out of a CPU problem"; instead, rethink the slow queries.

    ⚈  Do not strive for "Fixed" length rows in MyISAM. Usually it makes performance worse.

    ⚈  If more than 20% of the rows are needed, a table scan is faster than using the obvious INDEX.

    ⚈  key-value stores perform poorly. Toss most k-v into a JSON blob;

    ⚈  For fast loading use LOAD DATA or batched INSERTs.

    ⚈  Use 100-1000 rows per batch INSERT. (Important to limit in Replication)

    ⚈  Similarly, DELETE in chunks of 1000.

    ⚈  Cluster rows are 10x faster to access than random rows (fewer blocks to fetch)

    ⚈  Use SHOW CREATE TABLE, not DESCRIBE.

    ⚈  innodb_flush_log_at_trx_commit = 1 for safety; = 2 for speed (often a lot of speed)

    ⚈  How seriously to take optimization? 1K rows: yawn; 1M rows; serious; 1B rows: all of these tips, plus more.
If most rows can be eliminated by using an INDEX, then index is the efficient way to go. If not many rows can be eliminated, then the bouncing back and forth between the index and the data is likely to be more costly than ignoring the index and simply scanning the whole table. The 20% is approximate -- 10%-30% seems to be the typical range for the cutoff between the two algorithms.

If you have a flexible number of attributes on a table, and are puzzling over how to build the schema, you might think to use a separate "key-value" or "EAV" table. Or you might add lots of columns, only to stumble over the messiness of adding more columns. Many "NoSQL" competitors try to make this easy. Within MySQL, put all these attributes in a JSON blob. Recommend compressing the blob. (3:1 compression is typical for English text, XML, code, etc.) Make 'real' columns for the important attributes that need to be indexed. BTW, MariaDB has "dynamic columns"; it even allows indexing sparse column hidden in their key-value blob. More discussion of EAV issues and solutions.

If you can arrange for rows to be "adjacent" to each other, then one disk fetch will bring in many rows (10x speedup).

"Batched" INSERTs are where one INSERT statement has multiple rows. Nearly all of the performance benefit is in the first 100 rows; going beyond 1000 is really getting into 'diminishing returns'. Furthermore, in a Replication environment, a huge INSERT would cause the Slave to get 'behind'.

More on Large Deletes.


PARTITIONing

RoTsDiscussion

    ⚈  #1: Don't use PARTITIONing until you know how and why it will help.

    ⚈  Don't use PARTITION unless you will have >1M rows

    ⚈  No more than 50 PARTITIONs on a table (open, show table status, etc, are impacted) (fixed in 5.6.6?)

    ⚈  PARTITION BY RANGE is the only useful method.

    ⚈  SUBPARTITIONs are not useful.

    ⚈  The partition field should not be the field first in any key.

    ⚈  It is OK to have an AUTO_INCREMENT as the first part of a compound key, or in a non-UNIQUE index.
It is so tempting to believe that PARTITIONing will solve performance problems. But it is so often wrong.

PARTITIONing splits up one table into several smaller tables. But table size is rarely a performance issue. Instead, I/O time and indexes are the issues.

Perhaps the most common use case where PARTITIONing shines is the the dataset where "old" data is deleted from the table. RANGE PARTITIONing by day (or other unit of time) lets you do a nearly instantaneous DROP PARTITION plus REORGANIZE PARTITION instead of a much slower DELETE.

An AUTO_INCREMENT column must be the first column in some index. (That lets the engine find the 'next' value when opening the table.) It does not have to be the only field, nor does it have to be PRIMARY or UNIQUE. If it is not UNIQUE, you _could_ INSERT a duplicate id if you _explicitly_ provide the number.


Memory Usage

RoTsDiscussion

    ⚈  70% of RAM for innodb_buffer_pool_size (when using just InnoDB)

    ⚈  20% of RAM for key_buffer_size (when using just MyISAM)

    ⚈  Leave other memory tunables alone

    ⚈  Do NOT let mysql swap

    ⚈  thread_cache_size -- A small, non-zero, number (like 10) is good.

    ⚈  If (Opened_tables / Uptime) > 1/sec, increase table_open_cache.

    ⚈  Turn off the Query Cache. Type=off and size=0
MySQL performance depends on being in control of its use of RAM. The biggest pieces are the caches for MyISAM or InnoDB. These caches should be tuned to use a large chunk of RAM. Other things that can be tuned rarely matter much, and the default values in my.cnf (my.ini) tend to be "good enough".

The "Query Cache" is totally distinct from the key_buffer and the buffer_pool. ALL QC entries for one table are purged when ANY change to that table occurs. Hence, if a table is being frequently modified, the QC is virtually useless.

Tuning memory settings


Character Sets

RoTsDiscussion

    ⚈  Use SET NAMES utf8 (or equivalent).

    ⚈  Use only ascii and utf8 (or utf8mb4 in 5.5.3)

    ⚈  utf8_unicode_ci > utf8_general_ci > utf8_bin

    ⚈  Debug stored data via HEX(col), LENGTH(col), CHAR_LENGTH(col)

    ⚈  Do not use utf8 for hex or ascii strings (GUID, md5, ip address, country code, postal code, etc.)
Too many people figure that MySQL will 'just work' when it comes to utf8. Well, it doesn't. And it is complex. And if you mess it up, it is complicated to un-mess up.

European accents take 1 byte in latin1; 2 in utf8. LENGTH(col) >= CHAR_LENGTH(col): with European text '=' for latin1, '>' for utf8.

Character set / collation debugging guide


Datatypes - Directly supported

RoTsDiscussion

    ⚈  INT(5) is not what you think. (See SMALLINT, etc)

    ⚈  FLOAT(7,2) -- No; just say FLOAT

    ⚈  INT, TIMESTAMP, FLOAT -- 4 bytes. etc

    ⚈  Almost never use CHAR instead of VARCHAR.

    ⚈  Do not have separate DATE and TIME columns, nor separate YEAR, MONTH, etc.

    ⚈  Before using BIGINT (8 bytes), ask whether you really need such a big range.

    ⚈  Most INTs should be UNSIGNED

    ⚈  Most columns should be NOT NULL

    ⚈  TIMESTAMP is half the size of DATETIME (changing in 5.6)

    ⚈  VARCHAR(255) has some drawbacks over VARCHAR(11)

    ⚈  Overlapping time ranges: WHERE a.start < b.end AND a.end > b.start

    ⚈  Don't be surprised by AUTO_INCREMENT values after uncommon actions.
If you have a million rows in a table, then the space difference between INT (4 bytes) and TINYINT (1 byte) is 3MB. So, if you have large tables, learn the sizes of each datatype, and pick the datatypes with an eye to minimizing the table size. Smaller --> More cacheable --> Faster.

An AUTO_INCREMENT is very non-random, at least for inserting. Each new row will be on the 'end' of the table. That is, the last block is "hot spot". Thanks to caching very little I/O is needed for an AUTO_INCREMENT index.

VARCHAR(255) for everything is tempting. And for "small" tables it won't hurt. For large tables one needs to consider what happens during the execution of complex SELECTs. If a "temporary" table is implicitedly generated, the VARCHAR will take 767 bytes in the temp table (2+3*255) bytes. 2=VAR overhead, 3=utf8 expansion, 255=your limit.

A DELETE of the last row may or many not burn that AUTO_INCREMENT id. INSERT IGNORE burns ids because it allocates values before checking for duplicate keys. A Slave may see InnoDB ids arriving out of order (because transactions arrive in COMMIT order). A ROLLBACK (explicit or implicit) will burn any ids already allocated to INSERTs. REPLACE = DELETE + INSERT, so the INSERT comments apply to REPLACE. After a crash, the next id to be assigned may or may not be what you expect; this varies with Engine.



Datatypes - Implicit

RoTsDiscussion

    ⚈  Money -- Do not use FLOAT/DOUBLE. DECIMAL(13,4) (8 bytes) is probably wide enough for any currency this decade.

    ⚈  GUID/UUID/MD5, as a key, will perform poorly when the table is big.

    ⚈  Store GUID/UUID/MD5 in BINARY, not VARCHAR.

    ⚈  IP addresses -- don't ignore IPv6, it's here now! VARBINARY(39) or BINARY(16)

    ⚈  99% of SEQUENCE uses can/should be converted to AUTO_INCREMENT.

    ⚈  "Don't Queue it, just Do it" -- A database does not make a good queuing system

    ⚈  Store images in a table? No clearcut decision.

    ⚈  Do NOT store credit card numbers, SSNs, etc, until you learn the legal ramifications of your db being hacked!

    ⚈  Latitude/Longitude -- to distinguish houses, degrees * 10000 (MEDIUMINT) or DECIMAL with 4 decimal places

    ⚈  DOUBLE for latitude and longitude -- do you really need so much precision as to distinguish hairs on a flea?
GUIDs (etc) are very random. A big table with an index on such a field will be costly to maintain. That is, the 'next' GUID to be inserted is not likely to be in an index block that is currently cached.

Since GUID, UUID, MD5, and SHA1 are fixed length, VAR is not needed. If they are in hex, don't bother with utf8; use BINARY or CHAR CHARSET ascii.

Images could be stored in BLOB (not TEXT). This better assures referential integrity (not accidentally deleting the metadata or image, but not both). On the other hand, it is clumsy. With files, an img tag can point directly to the image on disk.

Efficient algorithm for finding "nearest" with latitude/longitude


Hardware

RoTsDiscussion

    ⚈  #1: "Count the disk hits" -- For I/O-bound queries this is the metric to use.

    ⚈  A schema solution is usually better than a hardware solution for performance.

    ⚈  Plain disks can handle 100 reads/writes per second

    ⚈  RAID helps by a multiple (the number of drives)

    ⚈  RAID with BBWC: writes are "free"; sync_binlog = 1

    ⚈  Use disk striping instead of manually spreading files across drives

    ⚈  O_DIRECT or O_ALL_DIRECT

    ⚈  Noop or Deadline, not CFQ

    ⚈  SSD: innodb_flush_neighbors = 0; mount nobarrier

    ⚈  Hardware cannot make up for schema/query design flaws.

    ⚈  HyperThreading and >8 cores degrade performance. (Changes coming in XtraDB, 5.6, MariaDB, etc)

    ⚈  A single connection will not use more than one core. Not even with UNION or PARTITION.

    ⚈  Don't put a cache in front of a cache

    ⚈  10x speed up when disk blocks are cached, so... Time a query twice -- first will get things cached, second will do no I/O

    ⚈  Benchmark with "SELECT SQL_NO_CACHE ..." (to avoid Query cache)

Because I/O is so much slower than CPUs, the first few queries on a 'cold' system take longer. This can be demonstrated by running a SELECT twice. (Be sure to avoid the Query cache.) The first run will fetch data from disk; the second will find everything cached -- roughly 10x faster.

"Count the disk hits": For large databases, the I/O dominates the time consumed -- often by a factor of 10. So, focus on how to minimize I/O, "cluster" data, create indexes that are more effective, etc.


Data Warehouse

RoTsDiscussion

    ⚈  #1: Create Summary Table(s) ("materialized views")

    ⚈  Look into 3rd party solutions: Akiban, InfoBright, TokuDB

    ⚈  Normalize, but don't over-normalize.

    ⚈  Do not normalize "continuous" values -- dates, floats, etc -- especially if you will do range queries

    ⚈  The average of averages is (usually) mathematically incorrect.

    ⚈  InfoBright (3rd party) -- 10:1 compression; all columns automatically indexed

    ⚈  TokuDB (3rd party) -- 3:1 compression; faster loading ("fractal" technology)

    ⚈  Range PARTITION the Fact table on a unit of time (to make DELETE efficient).

    ⚈  Use the smallest practical datatype for each fields (to shrink the 'fact' table).

    ⚈  Use InnoDB. That way, recovery from a power failure will be fast and painless.

    ⚈  Don't have any indexes other than an AUTO_INCREMENT PRIMARY KEY for the fact table. That way, INSERTs into it will be fast. Periodic augmentation of the summary table(s) can use that to keep track of where they "left off".
Data Warehousing usually has "reports". These tend to be COUNTs, SUMs, AVERAGEs, etc, of large amounts of data, broken down by hours/days/weeks, together with some other "dimensions" like department, country, product, etc.

Doing such reports against the raw ("Fact") table is costly because of the I/O to read lots of that table. Creating and maintaining "Summary table" is a technique for generating reports much more efficiently (typically 10x-100x faster).

A Summary table has usually has, say, PRIMARY KEY(product, day), plus other columns that are COUNTs, SUMs, etc, of metrics for the given product+day. A report reads the Summary table, not the Fact table, and it finishes any further arithmetic. A Summary table based on days can be used to genearte a weekly report by suitable SUMs and GROUP BY. AVERAGEs should be done by SUM(sum)/SUM(count).

Normalizing dates runs afoul of the 20% rule, plus making it impossible to do range scans.


Miscellany

RoTsDiscussion

    ⚈  MySQL can run 1000 qps. (just a RoT; YMMV)

    ⚈  The SlowLog is the main clue into performance problems.

    ⚈  1000 Databases or tables is a clue of poor schema design

    ⚈  100K Databases or tables will run very slowly because of OS overhead

    ⚈  < 10% improvement -- don't bother. Exception: shrink the datatypes before deploying

    ⚈  Beware of SQL injection

    ⚈  If you can't finish an InnoDB transaction in 5 seconds, redesign it.

    ⚈  MySQL has many builtin 'hard' limits; you will not hit any of them.

    ⚈  An excessive MaxClients (Apache) can cause trouble with max_connections

    ⚈  Connection pooling is generally not worth the effort.

    ⚈  SBR vs RBR -- too many variables to make a call

    ⚈  A Slave can have only one Master.

    ⚈  Do all ALTERs in a single statement. Exceptions: PARTITION, NDB

    ⚈  ALTER to add an ENUM option is efficient. (This has not always been the case.)

    ⚈  "Load average" often raises false alarms.

    ⚈  When Threads_running > 10, you _may_ be in serious trouble.

    ⚈  SHOW PROCESSLIST with some threads "Locked" -- some other thread is hogging something.

    ⚈  >90% CPU --> investigate queries/indexes. (The SlowLog also catches such.)

    ⚈  >90% of one core -- since MySQL won't use multiple cores in a single connection, this indicates an inefficient query. (Eg, 12% overall on an 8-core box is probably consuming one core.)

    ⚈  >90% I/O -- tuning, overall schema design, missing index, etc.

    ⚈  "NoSQL" is a catchy phrase looking for a definition. By the time NoSQL gets a definition, it will look a lot like an RDBMS solution.
MySQL can run thousands of trivial queries on modern hardware. Some special benchmarks have driven InnoDB past 100K qps. At the other extreme, I have seen a query run for a month. 1000 qps is simply a RoT that applies to a lot of systems; but your mileage can really vary a lot.

Over-normalization can lead to inefficiencies. Why have a 4-byte INT as an id for the 200 countries in the world; simply use a 2-byte CHAR(2) CHARSET ascii. Don't normalize dates -- see Data Warehousing.

SQL Injection is where you take user input (say, from an HTML form) and insert it verbatim into a SQL statement. Some hacker will soon find that your site is not protecting itself and have his way with your data.

"SELECT *" will break your code tomorrow when you add another field. It is better to spell out the fields explicitly. (There is no noticeable performance difference.)

ALTER, in most situations, completely copies over the table and rebuilds all the indexes. For a huge table, the can take days. Doing two ALTERs means twice the work; A single ALTER statement with several operations in it. OPTIMIZE is similarly costly, and may not provide much benefit.

MariaDB 5.3's "Dynamic Columns" eats into a big excuse for "NoSQL".

Wednesday, March 13, 2013

MIXED mode replication was as following :


There have been many changes in the replication implementation between 5.1 and 5.5.
Many changes have been implemented to address bugs that were discovered.

Yes, I would suggest changing the bin logging format from statement to mixed. 


 Mixed mode works as follows:
Perform binary logging in statement mode
If a statement appears to be unsafe, change to row mode
after finishing the current operation/event fall back to statement

The advantage is that the server can detect statements which can possibly cause
binlogging to fail or create inconsistencies and avoid them by using row based format.

The disadvantage is that as compared to statement based logging, row based logging
is slower and the logs would be bigger in size. But in case where statement based logging
may cause the replication to break, the performance degradation may be acceptable.

Please refer to the following link for further details.

http://dev.mysql.com/doc/refman/5.5/en/binary-log-mixed.html




After upgrading to 5.5 version you will see more messages mentioning that statement-based replication is unsafe for particular queries. As you upgrade to even later version, it may cause more messages as the replication issues with non-deterministic queries become larger in number. So the question is whether mixed mode replication will fix this issue and are there any concerns. This is exactly what mixed mode replication attempts to do for you - offer you the best of both types of replication. When a statement is able to be replicated easily, it will use this format, but if the statement is non-deterministic, that is, may cause a different result to occur in some way on the slave, then it will use row-based replication. To use the mixed mode, you can simply change the binary log format and restart the master for it to take affect.

If you have any concerns about how it works or possible catches, I would recommend the following document:

MySQL Server Variable: binlog_format (Doc ID 1507802.1)


 


When MIXED mode is enabled, then ROW based logging will automatically be done for any statements which would NOT be safe to replicate via statements.

> so I would say MIXED is the way to go for us after upgrading to 5.5

Yes, I would use MIXED or ROW. If you use ROW then you can also safely use these options:
transaction-isolation = READ-COMMITTED
innodb_autoinc_lock_mode = 2


You can read more about these options here:
http://dev.mysql.com/doc/refman/5.5/en/set-transaction.html
http://dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_autoinc_lock_mode


These options will allow InnoDB to use far fewer locks -- locks necessary to ensure the stabilizability of statement based logs -- namely gap locks, insert intention locks, the table level AUTO_INC lock, etc.

Use the sql-log-bin session variable to turn off logging for the current session.

You can use the sql-log-bin session variable to turn off logging for the current session. Just simply enter:

SET sql_log_bin = 0;
 
and all queries on your current session will not be sent to the binary log. If you want to turn binary logging back on, run:

SET sql_log_bin = 1;
 
This is only for the currently running session you are in. All other queries from all other connections will still continued to be logged. Also, you must have SUPER privileges for this to work

Monday, March 11, 2013

What is the safest way to switch the binlog format at runtime?

What is the safest way to switch the binlog format at runtime? 

Because of the following warning in mysqld.log:
[Warning] Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted.
I want to switch the replication format to MIXED.
But according to the MySQL document:
Switching the replication format at runtime is not recommended when any temporary tables exist, because temporary tables are logged only when using statement-based replication, whereas with row-based replication they are not logged.
So, the question is how can I identify if there is any temporary tables exist to switch the binary log format safely?


Since a binlog will have a specific format at the moment you do this, you may decide not to gamble with the two formats together although MySQL built this feature.
To play it totally safe without a mysql restart, try the following:
FLUSH TABLES WITH READ LOCK;
FLUSH LOGS;
SET GLOBAL binlog_format = 'MIXED';
FLUSH LOGS;
UNLOCK TABLES;
 
This will leave the last binlog in the 'MIXED' format. The penultimiate (next to last) binlog exists merely bring closure the last binlog that was in the previous format.
All existing sessions prior to the first FLUSH LOGS; will start writing in the last binlog once UNLOCK TABLES; is executed.


If you do this to an active Master and there are one or more Slaves replicating from that Master, you need to be concerned about the relay logs being in the new format as well. Here is what you can do:

On the Slave, run STOP SLAVE;
On the Master run these:
FLUSH TABLES WITH READ LOCK;
FLUSH LOGS;
SET GLOBAL binlog_format = 'MIXED';
FLUSH LOGS;
UNLOCK TABLES;
On the Slave, run START SLAVE;
Running STOP SLAVE; and START SLAVE; rotates the relay logs and causes the new entries to be replicated whichever format it comes. You may want to apply the binlog_format change in the slave as well.

Relay logs can be 'deleted' but mysql should manage it automatically.

Relay logs can be 'deleted' but mysql should manage it automatically.
One way to do this is to check for the value of relay_log_purge.

It should be set to 1 if you want mysql to manage them:
set global relay_log_purge=1;
 
You would probably need to flush the logs:
flush logs;
This does not affect the binary logs.

Monday, March 4, 2013

The Binary log can provide valuable information

The Binary log can provide valuable information about the frequency of per table DML statements.

This simple one line Linux command can provide valuable output:

$ mysqlbinlog /path/to/mysql-bin.000999 | \
grep -i -e "^update" -e "^insert" -e "^delete" -e "^replace" -e "^alter" | \
cut -c1-100 | tr '[A-Z]' '[a-z]' | \
sed -e "s/\t/ /g;s/\`//g;s/(.*$//;s/ set .*$//;s/ as .*$//" | sed -e "s/ where .*$//" | \
sort | uniq -c | sort -nr

33389 update e_acc
17680 insert into r_b
17680 insert into e_rec
14332 insert into rcv_c
13543 update e_rec
10805 update loc
3339 insert into r_att
2781 insert into o_att