- Simply using a
partition_options
clause withALTER TABLE
on a partitioned table repartitions the table according to the partitioning scheme defined by thepartition_options
. This clause always begins withPARTITION BY
, and follows the same syntax and other rules as apply to thepartition_options
clause forCREATE TABLE
(see Section 13.1.17, “CREATE TABLE
Syntax”, for more detailed information), and can also be used to partition an existing table that is not already partitioned. For example, consider a (nonpartitioned) table defined as shown here:CREATE TABLE t1 ( id INT, year_col INT );
This table can be partitioned byHASH
, using theid
column as the partitioning key, into 8 partitions by means of this statement:ALTER TABLE t1 PARTITION BY HASH(id) PARTITIONS 8;
The table that results from using anALTER TABLE ... PARTITION BY
statement must follow the same rules as one created usingCREATE TABLE ... PARTITION BY
. This includes the rules governing the relationship between any unique keys (including any primary key) that the table might have, and the column or columns used in the partitioning expression, as discussed in Section 18.5.1, “Partitioning Keys, Primary Keys, and Unique Keys”. TheCREATE TABLE ... PARTITION BY
rules for specifying the number of partitions also apply toALTER TABLE ... PARTITION BY
.ALTER TABLE ... PARTITION BY
became available in MySQL 5.1.6.Thepartition_definition
clause forALTER TABLE ADD PARTITION
supports the same options as the clause of the same name for theCREATE TABLE
statement. (See Section 13.1.17, “CREATE TABLE
Syntax”, for the syntax and description.) Suppose that you have the partitioned table created as shown here:CREATE TABLE t1 ( id INT, year_col INT ) PARTITION BY RANGE (year_col) ( PARTITION p0 VALUES LESS THAN (1991), PARTITION p1 VALUES LESS THAN (1995), PARTITION p2 VALUES LESS THAN (1999) );
You can add a new partitionp3
to this table for storing values less than2002
as follows:ALTER TABLE t1 ADD PARTITION (PARTITION p3 VALUES LESS THAN (2002));
DROP PARTITION
can be used to drop one or moreRANGE
orLIST
partitions. This statement cannot be used withHASH
orKEY
partitions; instead, useCOALESCE PARTITION
(see below). Any data that was stored in the dropped partitions named in thepartition_names
list is discarded. For example, given the tablet1
defined previously, you can drop the partitions namedp0
andp1
as shown here:ALTER TABLE t1 DROP PARTITION p0, p1;
NoteDROP PARTITION
does not work with tables that use theNDBCLUSTER
storage engine. See Section 18.3.1, “Management ofRANGE
andLIST
Partitions”, and Section 17.1.6, “Known Limitations of MySQL Cluster”.ADD PARTITION
andDROP PARTITION
do not currently supportIF [NOT] EXISTS
.Renames of partitioned table are supported. You can rename individual partitions indirectly usingALTER TABLE ... REORGANIZE PARTITION
; however, this operation makes a copy of the partition's data..COALESCE PARTITION
can be used with a table that is partitioned byHASH
orKEY
to reduce the number of partitions bynumber
. Suppose that you have created tablet2
using the following definition:CREATE TABLE t2 ( name VARCHAR (30), started DATE ) PARTITION BY HASH( YEAR(started) ) PARTITIONS 6;
You can reduce the number of partitions used byt2
from 6 to 4 using the following statement:ALTER TABLE t2 COALESCE PARTITION 2;
The data contained in the lastnumber
partitions will be merged into the remaining partitions. In this case, partitions 4 and 5 will be merged into the first 4 partitions (the partitions numbered 0, 1, 2, and 3).To change some but not all the partitions used by a partitioned table, you can useREORGANIZE PARTITION
. This statement can be used in several ways:- To merge a set of partitions into a single partition. This can be done by naming several partitions in the
partition_names
list and supplying a single definition forpartition_definition
. - To split an existing partition into several partitions. You can accomplish this by naming a single partition for
partition_names
and providing multiplepartition_definitions
. - To change the ranges for a subset of partitions defined using
VALUES LESS THAN
or the value lists for a subset of partitions defined usingVALUES IN
. - This statement may also be used without the
option on tables that are automatically partitioned usingpartition_names
INTO (partition_definitions
)HASH
partitioning to force redistribution of data. (Currently, onlyNDBCLUSTER
tables are automatically partitioned in this way.) This is useful in MySQL Cluster NDB 7.0 and later where, after you have added new MySQL Cluster data nodes online to an existing MySQL Cluster, you wish to redistribute existing MySQL Cluster table data to the new data nodes. In such cases, you should invoke the statement with theONLINE
option; in other words, as shown here:ALTER ONLINE TABLE
table
REORGANIZE PARTITION;You cannot perform other DDL concurrently with online table reorganization—that is, no other DDL statements can be issued while anALTER ONLINE TABLE ... REORGANIZE PARTITION
statement is executing. For more information about adding MySQL Cluster data nodes online, see Section 17.5.13, “Adding MySQL Cluster Data Nodes Online”.ALTER ONLINE TABLE ... REORGANIZE PARTITION
does not work with tables which were created using theMAX_ROWS
option, because it uses the constantMAX_ROWS
value specified in the originalCREATE TABLE
statement to determine the number of partitions required, so no new partitions are created. Beginning with MySQL Cluster NDB 7.0.32 and MySQL Cluster NDB 7.1.21, you can useALTER ONLINE TABLE ... MAX_ROWS=
to increase the maxmimum number of rows for the table; after this,rows
ALTER ONLINE TABLE ... REORGANIZE PARTITION
can use this new, larger value to increase the number of partitions. The value ofrows
must be greater than the value specified forMAX_ROWS
in the originalCREATE TABLE
statement for this to work.Attempting to useREORGANIZE PARTITION
without the
option on explicitly partitioned tables results in the error REORGANIZE PARTITION without parameters can only be used on auto-partitioned tables using HASH partitioning.partition_names
INTO (partition_definitions
)
NoteFor partitions that have not been explicitly named, MySQL automatically provides the default namesp0
,p1
,p2
, and so on. As of MySQL 5.1.7, the same is true with regard to subpartitions.For more detailed information about and examples ofALTER TABLE ... REORGANIZE PARTITION
statements, see Section 18.3.1, “Management ofRANGE
andLIST
Partitions”.
Collection of softwares, news, articles, commands, reports, blah, blah, blah
Search This Blog
Sunday, July 21, 2013
MySQL :: MySQL 5.1 Reference Manual :: 13.1.7.1 ALTER TABLE Partition Operations
MySQL :: MySQL 5.1 Reference Manual :: 13.1.7.1 ALTER TABLE Partition Operations:
MySQL :: Re: How to create partition on existing table?
MySQL :: Re: How to create partition on existing table?:
you can use ALTER TABLE to add partitioning to the table,
keep in mind though that this will actually create the
new partitioned table first, then copy over all the
existing data, and finally drop the old unpartitioned
table. So this operation may take a while and will
temporarily use twice the disk space (which in the case
of InnoDB is not given back to the operating system ...)
you can use ALTER TABLE to add partitioning to the table,
keep in mind though that this will actually create the
new partitioned table first, then copy over all the
existing data, and finally drop the old unpartitioned
table. So this operation may take a while and will
temporarily use twice the disk space (which in the case
of InnoDB is not given back to the operating system ...)
MySQL :: Partitioning with Dates in MySQL 5.1
MySQL :: Partitioning with Dates in MySQL 5.1:
Partitioning with Dates in MySQL 5.1
Many of you have responded very positively on the first and second partitioning articles that I wrote some time back. It’s clear that a lot of you are excited about the upcoming MySQL 5.1 release, and in particular, the new data partitioning feature that’s being offered. That’s no surprise because, as we covered in the previous two articles, partitioning has some excellent performance and management benefits for anyone who’s building data warehouses or other large databases with MySQL.
However, I’ve gotten some feedback indicating a disappointment that MySQL 5.1 doesn’t support partitioning on a native date/time column. Given that many data warehouses and other large databases try and segment data by some date-based algorithm – especially via the RANGE partitioning style – it’s understandable that a number of you are wondering if partitioning in 5.1 will meet the needs of your particular business intelligence application that relies on time-based functions.
It’s true that in MySQL 5.1 the actual partitioning key (the element used by the MySQL partition engine to horizontally divide the rows across the various partitions defined on a table or index) must be an integer value. But that doesn’t mean you can’t partition by date in MySQL 5.1 – it just requires the proper selection between a couple of MySQL date functions and you’ll be in business.
Let’s take a look at how to properly partition by dates in MySQL 5.1 and also showcase how not to try and partition by date, so you won’t waste time and effort on something that doesn’t give you the results that you’re looking for.
How Not to Partition by Date
I always appreciate it when someone shows me where not to step, so let’s first see what you don’t want to try when it comes to partitioning by date in MySQL 5.1. If you try and define a table with a partition key that references a date datatype, MySQL will quickly kick back an error:
mysql> create table rms (d date) -> partition by range (d) -> (partition p0 values less than ('1995-01-01'), -> partition p1 VALUES LESS THAN ('2010-01-01')); ERROR 1064 (42000): VALUES value must be of same type as partition function near '), partition p1 VALUES LESS THAN ('2010-01-01'))' at line 3
Because MySQL DBAs and developers are very clever and innovative, the first thing they'll do is try a workaround for the integer-only restriction in MySQL 5.1 regarding the partition key. For example, here's one way to get around the restriction:
mysql> CREATE TABLE part_date1 -> ( c1 int default NULL, -> c2 varchar(30) default NULL, -> c3 date default NULL) engine=myisam -> partition by range (cast(date_format(c3,'%Y%m%d') as signed)) -> (PARTITION p0 VALUES LESS THAN (19950101), -> PARTITION p1 VALUES LESS THAN (19960101) , -> PARTITION p2 VALUES LESS THAN (19970101) , -> PARTITION p3 VALUES LESS THAN (19980101) , -> PARTITION p4 VALUES LESS THAN (19990101) , -> PARTITION p5 VALUES LESS THAN (20000101) , -> PARTITION p6 VALUES LESS THAN (20010101) , -> PARTITION p7 VALUES LESS THAN (20020101) , -> PARTITION p8 VALUES LESS THAN (20030101) , -> PARTITION p9 VALUES LESS THAN (20040101) , -> PARTITION p10 VALUES LESS THAN (20100101), -> PARTITION p11 VALUES LESS THAN MAXVALUE ); Query OK, 0 rows affected (0.01 sec)
Bingo! MySQL took it. Here’s another possible workaround:
mysql> CREATE TABLE part_date2 -> ( c1 int default NULL, -> c2 varchar(30) default NULL, -> c3 date default NULL) engine=myisam -> partition by range (YEAR(C3)*100+MONTH(C3)) -> (PARTITION p0 VALUES LESS THAN (199501), -> PARTITION p1 VALUES LESS THAN (199601) , -> PARTITION p2 VALUES LESS THAN (199701) , -> PARTITION p3 VALUES LESS THAN (199801) , -> PARTITION p4 VALUES LESS THAN (199901) , -> PARTITION p5 VALUES LESS THAN (200001) , -> PARTITION p6 VALUES LESS THAN (200101) , -> PARTITION p7 VALUES LESS THAN (200201) , -> PARTITION p8 VALUES LESS THAN (200301) , -> PARTITION p9 VALUES LESS THAN (200401) , -> PARTITION p10 VALUES LESS THAN (201001), -> PARTITION p11 VALUES LESS THAN MAXVALUE ); Query OK, 0 rows affected (0.02 sec)
MySQL took that too. At this point, you might be feeling pretty crafty as you’ve outwitted MySQL into partitioning by a date column. But watch what happens when we fill the tables with millions of rows of data and then see how each reacts with a standard range-based query:
mysql> explain partitions -> select count(*) from part_date1 where -> c3 > date '1995-01-01' and c3 < date '1995-12-31'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: part_date1 partitions: p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11 type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 8100000 Extra: Using where 1 row in set (0.00 sec) mysql> explain partitions -> select count(*) from part_date2 where -> c3 > date '1995-01-01' and c3 < date '1995-12-31'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: part_date1 partitions: p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11 type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 8100000 Extra: Using where 1 row in set (0.00 sec)
Yikes! What happened? MySQL is doing a full table scan rather than just scanning the partition(s) that contain the data we need. The issue is that you’ve forgotten the second part of the equation when it comes to leveraging date-based partitioning (or any partitioning style really), which is the MySQL optimizer. Yes, you can divide your data using any number of date-to-integer conversion techniques, but that doesn’t mean you’ll get the big bang when it comes to using partitioning, which is partition pruning – the act where MySQL only references the partitions needed to fulfill a particular query. The optimizer’s partition pruning mechanism is the actual hero that accomplishes the performance speed-up’s in partitioned vs. non-partitioned tables, and without it, you might as well go back to using non-partitioned tables if it’s an increase in performance you’re looking for.
But take heart – there is a way to partition by date and get partition pruning.
How to Partition by Date
The MySQL optimizer will recognize two date-based functions for partition pruning purposes:
- 1.TO_DAYS()
- 2.YEAR()
If you partition date columns using either of these functions, you can both define a partitioned table by date and get the benefit of partition pruning from the optimizer (provided, of course, that your query is written in such a way so as to take advantage of the optimizer’s ability to prune partitions). For example, we can redefine the tables used in our previous example using both the TO_DAYS() and YEAR() date-based functions:
mysql> CREATE TABLE part_date3 -> ( c1 int default NULL, -> c2 varchar(30) default NULL, -> c3 date default NULL) engine=myisam -> partition by range (to_days(c3)) -> (PARTITION p0 VALUES LESS THAN (to_days('1995-01-01')), -> PARTITION p1 VALUES LESS THAN (to_days('1996-01-01')) , -> PARTITION p2 VALUES LESS THAN (to_days('1997-01-01')) , -> PARTITION p3 VALUES LESS THAN (to_days('1998-01-01')) , -> PARTITION p4 VALUES LESS THAN (to_days('1999-01-01')) , -> PARTITION p5 VALUES LESS THAN (to_days('2000-01-01')) , -> PARTITION p6 VALUES LESS THAN (to_days('2001-01-01')) , -> PARTITION p7 VALUES LESS THAN (to_days('2002-01-01')) , -> PARTITION p8 VALUES LESS THAN (to_days('2003-01-01')) , -> PARTITION p9 VALUES LESS THAN (to_days('2004-01-01')) , -> PARTITION p10 VALUES LESS THAN (to_days('2010-01-01')), -> PARTITION p11 VALUES LESS THAN MAXVALUE ); Query OK, 0 rows affected (0.00 sec) mysql> CREATE TABLE part_date4 -> ( c1 int default NULL, -> c2 varchar(30) default NULL, -> c3 date default NULL) engine=myisam -> PARTITION BY RANGE (year(c3)) -> (PARTITION p0 VALUES LESS THAN (1995), -> PARTITION p1 VALUES LESS THAN (1996) , -> PARTITION p2 VALUES LESS THAN (1997) , -> PARTITION p3 VALUES LESS THAN (1998) , -> PARTITION p4 VALUES LESS THAN (1999) , -> PARTITION p5 VALUES LESS THAN (2000) , -> PARTITION p6 VALUES LESS THAN (2001) , -> PARTITION p7 VALUES LESS THAN (2002) , -> PARTITION p8 VALUES LESS THAN (2003) , -> PARTITION p9 VALUES LESS THAN (2004) , -> PARTITION p10 VALUES LESS THAN (2010), -> PARTITION p11 VALUES LESS THAN MAXVALUE ); Query OK, 0 rows affected (0.02 sec)
If we fill both tables with the same amount of data as the tables we created with non-prunable partitioning keys and issue range-based queries against the newly loaded well-partitioned tables, we’ll find a big difference when it comes to what the MySQL optimizer does:
mysql> explain partitions -> select count(*) from part_date3 where -> c3 > date '1995-01-01' and c3 < date '1995-12-31'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: part_date3 partitions: p1 type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 808431 Extra: Using where 1 row in set (0.00 sec) mysql> explain partitions -> select count(*) from part_date4 where -> c3 > date '1995-01-01' and c3 < date '1995-12-31'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: part_date4 partitions: p1 type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 200000 Extra: Using where 1 row in set (0.00 sec)
Notice only the p1 partition is being scanned in these two tables, whereas all partitions were being checked in the tables that had partition keys the MySQL optimizer couldn’t use for partition pruning. A quick test run shows partition pruning in action along with the resulting performance difference:
mysql> select count(*) from part_date3 where -> c3 > date '1995-01-01' and c3 < date '1995-12-31'; +----------+ | count(*) | +----------+ | 805114 | +----------+ 1 row in set (4.11 sec) mysql> select count(*) from part_date1 where -> c3 > date '1995-01-01' and c3 < date '1995-12-31'; +----------+ | count(*) | +----------+ | 805114 | +----------+ 1 row in set (40.33 sec)
A 90% decrease in response time for the query referencing table part_date3 over the query requesting data from table part_date1 spells out pretty clearly why it’s important to use date functions that the MySQL optimizer can utilize when defining date-based partitioned tables.
Conclusion
While a restriction does exist in MySQL 5.1 that says partitioned tables must be partitioned via an integer-based value, it doesn’t mean you can’t partition tables by date or get the benefits of partition pruning. You must, however, ensure that you’re using date conversion functions in the defined partitioning key for the table that the MySQL optimizer can make use of.
For more information on partitioning in general, please check out our online reference manual at http://dev.mysql.com/doc/refman/5.1/en/partitioning.html. Also, don’t forget about our MySQL forums, which have a special section devoted to partitioning at http://forums.mysql.com/list.php?106.
Of course I’d be remiss if I didn’t encourage you to download the latest copy of MySQL 5.1 (which is now in beta) today and give partitioning a try. I’d certainly love to hear how you’re using partitioning and other new features in MySQL 5.1.
Thanks, as always, for supporting MySQL!
Monday, July 15, 2013
json - json_encode/json_decode - returns stdClass instead of Array in PHP - Stack Overflow
json - json_encode/json_decode - returns stdClass instead of Array in PHP - Stack Overflow:
Take a closer look at the second parameter of
Take a closer look at the second parameter of
json_decode($json, $assoc, $depth)
athttp://docs.php.net/json_decode$arrayDecoded = json_decode($arrayEncoded, true);
gives you an array.
configuration - Is there an application to manage config files in the /etc folder with version control software? - Ask Ubuntu
configuration - Is there an application to manage config files in the /etc folder with version control software? - Ask Ubuntu:
The package
etckeeper
is specifically intended to manage your configuration files (located in the/etc
directory) under a version control system(VCS).
What is etckeeper?:
etckeeper is a collection of tools to let /etc be stored in a git, mercurial, darcs, or bzr repository. It hooks into apt (and other package managers) to automatically commit changes made to /etc during package upgrades.
How to do I install
etckeeper
?:
To install the package, click on the link provided below to install in Ubuntu Software Center(USC), or run
sudo apt-get install etckeeper
from the terminal(Ctrl-Alt-t).
How do I change
etckeeper
from the default VCS(bzr) to use git
?:
By default,
etckeeper
uses Bazaar(bzr) for version control management. This can be changed in the configuration file /etc/etckeeper/etckeeper.conf
.
First, you will need to have git installed.
sudo apt-get install git
sudo apt-get install git-core
Now, you will need to edit your
etckeeper.conf
file to change your VCS. Here is what the first five lines of the default configuration should look like:# The VCS to use.
# VCS="hg"
# VCS="git"
VCS="bzr"
# VCS="darcs"
This is where to switch to using git. Open a text editor (i.e.
gksu gedit /etc/etc/keeper/etckeeper.conf
) and change the file to look like this:# The VCS to use.
# VCS="hg"
VCS="git"
# VCS="bzr"
# VCS="darcs"
Uncomment the line
VCS="git"
and comment out the line VCS="bzr"
Now, run
sudo etckeeper init
to initialize your repository.
As mentioned here:
This sets up version control for all files in/etc
. You don't need to do anything else to manage etckeeper; you only need to interact with it when you want to do a version control operation, such as referring to older files. Files are automatically committed before and after each run of apt and every night (this is configurable).
Additional information on etckeeper:
also
MySQL, Linux, and Thread Caching (by Jeremy Zawodny)
MySQL, Linux, and Thread Caching (by Jeremy Zawodny): "SET GLOBAL wait_timeout=60;"
Wow, it's been a busy week. I was totally swamped for several days dealing with the remember.yahoo.com MySQL servers and related stuff. And then I used a day or two to recover (sleep, shower, etc).
Anyway, I made some interesting discoveries along the way. The most surprising one had to do with thread caching on Linux when you have a busy MySQL server--busy in a particular way, mind you.
You see, we had a single master server which all the web servers could connect to (using PHP) whenever someone made a change. That includes creating a tile (there were several hundred thousand tiles created), approves a tile, marks one as "cool", and so on. All told, the master was quite busy.
Because there were between 20 and 45 front-end web servers during that time, and each could have had up to 70 apache processes that might have needed to connect, we faced a problem. That meant that the master needed to handle up to 3,150 connections in the worst case (that's 45 x 70). Most of the PHP code used mysql_pconnect() to hold persistent connections.
Rather than worry about how to do that, I made sure that the wait_timeout was set to a very low value: 15 seconds. That means MySQL would close any connection that was idle for more than 15 seconds. But I didn't realize the extent of the problem until I started getting reports from the web servers that the master was refusing connections. Why? Because I had set the maximum number of connections to a reasonable value in the master's my.cnf file:
set-variable = max_connections=180 set-variable = max_user_connections=140
And at that time, the wait_timeout was set to 600 seconds (10 minutes). Clearly that was a problem. There were a lot of idle clients holding connections and blocking out new clients from connecting and getting real work done.
What to do?
We could have stopped using mysql_pconnect(), but as you'll see, that wouldn't have solved the underlying problem.
I needed to adjust the settings. But I wasn't sure what values to use. And I really didn't want to keep stopping and starting the master. That would just suck. Then I remembered that we were running MySQL 4.0.4. I'd has a new feature that allows you to change most of the server settings on on the fly without restarting! Read about here, it in the on-line manual.
Excellent!
All I needed to do was execute a few variations on this command:
SET GLOBAL wait_timeout=60;
(with different values in the place of "60") to try and strike a balance between letting new clients in and kicking out already connected users too quickly.
Ultimately, I settled on a timeout of 15 seconds.
But that had an interesting and unanticipated side-effect. It meant that the Linux server was having to create new threads (MySQL is a multi-threaded server) at a very high rate. That sucks up a measurable amount of CPU time.
How much CPU time? By the time I got around to looking at the output of SHOW STATUS and seeing this:
| Threads_cached | 0 | | Threads_created | 270194 | | Threads_connected | 46 | | Threads_running | 28 |
Things were pretty bad. The machine had very little idle CPU time--probably 5-10% at the most. But it really wasn't doing that much work--maybe 40 queries per second. I was a bit puzzled. But that Threads_created number jumped out at me. It was high and increasing rapidly.
Luckily I remembered the thread_cache setting. So I decided to investigate (using the new syntax for examining server variables):
mysql> SELECT @@global.thread_cache_size; +---------------------+ | @@thread_cache_size | +---------------------+ | 0 | +---------------------+ 1 row in set (0.00 sec)
Uh oh. I never set the thread cache in my.cnf, so it has assumed the default. That's bad. It's like removing the pre-forking capabilities of Apache 1.3 and letting it get pounded on a busy web site. The "fork a new process for each new request" gets pretty expensive pretty quickly.
Ugh!
Luckily the thread cache is also tunable on the fly now. So all I had to do was this:
SET GLOBAL thread_cache_size=40;
I took a guess and figured that by caching 40 threads, we'd be saving a lot of work. And boy was I right!
In the other window, where I was running vmstat 1 I noticed a dramatic change. The idle CPU on the machine immediately went from 5-10% to 35-40%
If only I had thought of that sooner!
So the moral of the story is this: If you have a busy server that's getting a lot of quick connections, set your thread cache high enough that the Threads_created value in SHOW STATUS stops increasing. Your CPU will thank you.
I don't feel bad though. We were all going nuts to try and tune/optimize the code and servers while it was running and had very little sleep. Thread caching really wasn't the worst of our problems. But it became the worst after we had fixed all the bigger ones.
It was quite a learning experience.
tuning - MySQL lowering wait_timeout value to lower number of open connections - Server Fault
tuning - MySQL lowering wait_timeout value to lower number of open connections - Server Fault:
Lowering the value is pretty trivial without a mysql restart
Let's say you want to lower timeouts to 30 seconds
First, add this to my.cnf
[mysqld]
interactive_timeout=30
wait_timeout=30
Then, you can do something like this
mysql -uroot -ppassword -e"SET GLOBAL wait_timeout=30; SET GLOBAL interactive_timeout=30"
All DB Connections after this will timeout in 30 seconds
WARNING
Make sure to use explicitly use mysql_close. I do not trust Apache as most developers do. If not, sometimes, there is a race condition where Apache closes a DB Connection but does not inform mysqld and mysqld hold that connection open until it times out. Even worse, you may see TIME_WAITs more often. Choose your timeout values wisely.
UPDATE 2012-11-12 10:10 EDT
CAVEAT
After applying my posted suggestions, create a script called
/root/show_mysql_netstat.sh
with the following lines:netstat | grep mysql > /root/mysql_netstat.txt
cat /root/mysql_netstat.txt | awk '{print $5}' | sed 's/:/ /g' | awk '{print $2}' | sort -u > /root/mysql_netstat_iplist.txt
for IP in `cat /root/mysql_netstat_iplist.txt`
do
ESCOUNT=`cat /root/mysql_netstat.txt | grep ESTABLISHED | awk '{print $5}' | grep -c "${IP}"`
TWCOUNT=`cat /root/mysql_netstat.txt | grep TIME_WAIT | awk '{print $5}' | grep -c "${IP}"`
IPPAD=`echo "${IP}..................................." | cut -b -35`
(( ESCOUNT += 1000000 ))
(( TWCOUNT += 1000000 ))
ES=`echo ${ESCOUNT} | cut -b 3-`
TW=`echo ${TWCOUNT} | cut -b 3-`
echo ${IPPAD} : ESTABLISHED:${ES} TIME_WAIT:${TW}
done
echo ; echo
netstat -nat | awk '{print $6}' | sort | uniq -c | sort -n | sed 's/d)/d/'
When you run this, you should see something like this:
[root@*** ~]# /root/ShowConnProfiles.sh
10.48.22.4......................... : ESTABLISHED:00002 TIME_WAIT:00008
10.48.22.8......................... : ESTABLISHED:00000 TIME_WAIT:00002
10.64.51.130....................... : ESTABLISHED:00001 TIME_WAIT:00000
10.64.51.133....................... : ESTABLISHED:00000 TIME_WAIT:00079
10.64.51.134....................... : ESTABLISHED:00002 TIME_WAIT:00001
10.64.51.17........................ : ESTABLISHED:00003 TIME_WAIT:01160
10.64.51.171....................... : ESTABLISHED:00002 TIME_WAIT:00000
10.64.51.174....................... : ESTABLISHED:00000 TIME_WAIT:00589
10.64.51.176....................... : ESTABLISHED:00001 TIME_WAIT:00570
1 established
1 Foreign
11 LISTEN
25 ESTABLISHED
1301 TIME_WAIT
If you still see a lot of mysql
TIME_WAITs
for any given web server, here are two escalation steps to take:ESCALATION #1
Login to the offending web server and restart apache as follows:
service httpd stop
sleep 30
service httpd start
If necessary, do this to all the web servers
service httpd stop (on all web servers)
service mysql stop
sleep 120
service mysql start
service httpd start (on all web servers)
ESCALATION #2
You can force the OS to kill TIME_WAITs for mysql or any other app with the following:
SEC_TO_TIMEWAIT=1
echo ${SEC_TO_TIMEWAIT} > /proc/sys/net/ipv4/tcp_tw_recycle
echo ${SEC_TO_TIMEWAIT} > /proc/sys/net/ipv4/tcp_tw_reuse
This will make TIME_WAITs time out in 1 second.
Subscribe to:
Posts (Atom)