Mysql Query is not giving good performance - mysql

I have a fairly simple query in MySQL but it is taking around 170 minutes to execute.
Can anyone help me here? I am tired of applying indexes on various keys but no benefit.
Update
H20_AUDIENCE_ADDRESS_LOG L
Join
TEMP_V_3064446579 T
Using
( ZS_AUDIENCE_ID, ZS_SOURCE_OBJECT_ID, ZS_ADDRESS_TYPE_ID )
Set
ZS_ACTIVE_PERIOD_END_DT = '2015-08-14 15:05:48',
ZS_IS_ACTIVE_PERIOD = False
Where
ZS_IS_ACTIVE_PERIOD = True
And
L.ZS_ADDRESS_ID <> T.ZS_ADDRESS_ID
And
T.ZS_SOURCE_TIMESTAMP > L.ZS_SOURCE_TIMESTAMP;
Creates:
CREATE TABLE `H20_AUDIENCE_ADDRESS_LOG` (
`ZS_AUDIENCE_ADDRESS_LOG_ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`ZS_AUDIENCE_ID` bigint(20) unsigned NOT NULL,
`ZS_SOURCE_OBJECT_ID` int(10) unsigned NOT NULL,
`ZS_INSERT_DT` datetime NOT NULL,
`ZS_ADDRESS_TYPE_ID` tinyint(3) unsigned NOT NULL,
`ZS_ADDRESS_ID` bigint(20) unsigned NOT NULL,
`ZS_SOURCE_TIMESTAMP` datetime NOT NULL,
`ZS_ACTIVE_PERIOD_START_DT` datetime DEFAULT NULL,
`ZS_ACTIVE_PERIOD_END_DT` datetime DEFAULT NULL,
`ZS_IS_ACTIVE_PERIOD` bit(1) DEFAULT NULL,
`ZS_ACTIVE_PRIORITY_PERIOD_START_DT` datetime DEFAULT NULL,
`ZS_ACTIVE_PRIORITY_PERIOD_END_DT` datetime DEFAULT NULL,
`ZS_IS_ACTIVE_PRIORITY_PERIOD` bit(1) DEFAULT NULL,
PRIMARY KEY (`ZS_AUDIENCE_ADDRESS_LOG_ID`),
KEY `IX_H20_AUDIENCE_ADDRESS_LOG` (`ZS_AUDIENCE_ID`,`ZS_SOURCE_OBJECT_ID`,`ZS_ADDRESS_TYPE_ID`,`ZS_ADDRESS_ID`),
KEY `IX_ADDRESS_ID` (`ZS_ADDRESS_ID`,`ZS_IS_ACTIVE_PERIOD`)
) ENGINE=InnoDB AUTO_INCREMENT=22920801 DEFAULT CHARSET=utf8;
CREATE TABLE `TEMP_V_3064446579` (
`ZS_AUDIENCE_ID` bigint(20) unsigned NOT NULL,
`ZS_SOURCE_OBJECT_ID` int(10) unsigned NOT NULL,
`ZS_ADDRESS_TYPE_ID` tinyint(3) unsigned NOT NULL,
`ZS_ADDRESS_ID` bigint(20) unsigned NOT NULL,
`ZS_SOURCE_TIMESTAMP` datetime NOT NULL,
UNIQUE KEY `IX_TEMP_V_3064446579` (`ZS_AUDIENCE_ID`,`ZS_SOURCE_OBJECT_ID`,`ZS_ADDRESS_TYPE_ID`,`ZS_ADDRESS_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Both tables circa 3m records

Something like this should work:
UPDATE
`H20_AUDIENCE_ADDRESS_LOG` `L`
SET
`ZS_ACTIVE_PERIOD_END_DT` = '2015-08-14 15:05:48',
`ZS_IS_ACTIVE_PERIOD` = False
WHERE
`ZS_IS_ACTIVE_PERIOD` = True AND
EXISTS (
SELECT
1
FROM
`TEMP_V_3064446579` `T`
WHERE
`L`.`ZS_ADDRESS_ID` <> `T`.`ZS_ADDRESS_ID` AND
`T`.`ZS_SOURCE_TIMESTAMP` > `L`.`ZS_SOURCE_TIMESTAMP`
LIMIT 1
);

(The ZS_ makes the SQL hard to read; suggest removing it.)
In TEMP_V_3064446579, change UNIQUE to PRIMARY.
Change
KEY `IX_H20_AUDIENCE_ADDRESS_LOG` (`ZS_AUDIENCE_ID`,`ZS_SOURCE_OBJECT_ID`,
`ZS_ADDRESS_TYPE_ID`,`ZS_ADDRESS_ID`)
to
KEY `IX_H20_AUDIENCE_ADDRESS_LOG` (`ZS_AUDIENCE_ID`,`ZS_SOURCE_OBJECT_ID`,
`ZS_ADDRESS_TYPE_ID`,`ZS_ADDRESS_ID`,
`ZS_SOURCE_TIMESTAMP`)
If you have a new enough version, please provide EXPLAIN UPDATE .... If not, please provide EXPLAIN SELECT ... where the SELECT is derived from the UPDATE, but without the SET.

Related

Long running Mysql Query on Indexes and sort by clause

I have a very long running MySql query. The query simply joins two tables which are very huge
bizevents - Nearly 34 Million rows
bizevents_actions - Nearly 17 million rows
Here is the query:
select
bizevent0_.id as id1_37_,
bizevent0_.json as json2_37_,
bizevent0_.account_id as account_3_37_,
bizevent0_.createdBy as createdB4_37_,
bizevent0_.createdOn as createdO5_37_,
bizevent0_.description as descript6_37_,
bizevent0_.iconCss as iconCss7_37_,
bizevent0_.modifiedBy as modified8_37_,
bizevent0_.modifiedOn as modified9_37_,
bizevent0_.name as name10_37_,
bizevent0_.version as version11_37_,
bizevent0_.fired as fired12_37_,
bizevent0_.preCreateFired as preCrea13_37_,
bizevent0_.entityRefClazz as entityR14_37_,
bizevent0_.entityRefIdAsStr as entityR15_37_,
bizevent0_.entityRefIdType as entityR16_37_,
bizevent0_.entityRefName as entityR17_37_,
bizevent0_.entityRefType as entityR18_37_,
bizevent0_.entityRefVersion as entityR19_37_
from
BizEvent bizevent0_
left outer join BizEvent_actions actions1_ on
bizevent0_.id = actions1_.BizEvent_id
where
bizevent0_.createdOn >= '1969-12-31 19:00:01.0'
and (actions1_.action <> 'SoftLock'
and actions1_.targetRefClazz = 'com.biznuvo.core.orm.domain.org.EmployeeGroup'
and actions1_.targetRefIdAsStr = '1'
or actions1_.action <> 'SoftLock'
and actions1_.objectRefClazz = 'com.biznuvo.core.orm.domain.org.EmployeeGroup'
and actions1_.objectRefIdAsStr = '1')
order by
bizevent0_.createdOn;
Below are the table definitions -- As you see i have defined the indexes well enough on these two tables on all the search columns plus the sort column. But still my queries are running for very very long time. Appreciate any more ideas either with respective indexing.
-- bizevent definition
CREATE TABLE `bizevent` (
`id` bigint(20) NOT NULL,
`json` longtext,
`account_id` int(11) DEFAULT NULL,
`createdBy` varchar(50) NOT NULL,
`createdon` datetime(3) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`iconCss` varchar(50) DEFAULT NULL,
`modifiedBy` varchar(50) NOT NULL,
`modifiedon` datetime(3) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`version` int(11) NOT NULL,
`fired` bit(1) NOT NULL,
`preCreateFired` bit(1) NOT NULL,
`entityRefClazz` varchar(255) DEFAULT NULL,
`entityRefIdAsStr` varchar(255) DEFAULT NULL,
`entityRefIdType` varchar(25) DEFAULT NULL,
`entityRefName` varchar(255) DEFAULT NULL,
`entityRefType` varchar(50) DEFAULT NULL,
`entityRefVersion` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `IDXk9kxuuprilygwfwddr67xt1pw` (`createdon`),
KEY `IDXsf3ufmeg5t9ok7qkypppuey7y` (`entityRefIdAsStr`),
KEY `IDX5bxv4g72wxmjqshb770lvjcto` (`entityRefClazz`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- bizevent_actions definition
CREATE TABLE `bizevent_actions` (
`BizEvent_id` bigint(20) NOT NULL,
`action` varchar(255) DEFAULT NULL,
`objectBizType` varchar(255) DEFAULT NULL,
`objectName` varchar(255) DEFAULT NULL,
`objectRefClazz` varchar(255) DEFAULT NULL,
`objectRefIdAsStr` varchar(255) DEFAULT NULL,
`objectRefIdType` int(11) DEFAULT NULL,
`objectRefVersion` int(11) DEFAULT NULL,
`targetBizType` varchar(255) DEFAULT NULL,
`targetName` varchar(255) DEFAULT NULL,
`targetRefClazz` varchar(255) DEFAULT NULL,
`targetRefIdAsStr` varchar(255) DEFAULT NULL,
`targetRefIdType` int(11) DEFAULT NULL,
`targetRefVersion` int(11) DEFAULT NULL,
`embedJson` longtext,
`actions_ORDER` int(11) NOT NULL,
PRIMARY KEY (`BizEvent_id`,`actions_ORDER`),
KEY `IDXa21hhagjogn3lar1bn5obl48gll` (`action`),
KEY `IDX7agsatk8u8qvtj37vhotja0ce77` (`targetRefClazz`),
KEY `IDXa7tktl678kqu3tk8mmkt1mo8lbo` (`targetRefIdAsStr`),
KEY `IDXa22eevu7m820jeb2uekkt42pqeu` (`objectRefClazz`),
KEY `IDXa33ba772tpkl9ig8ptkfhk18ig6` (`objectRefIdAsStr`),
CONSTRAINT `FKr9qjs61id11n48tdn1cdp3wot` FOREIGN KEY (`BizEvent_id`) REFERENCES `bizevent` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;>
By the way we are using Amazon RDS 5.7.33 MySql version. 16 GB RAM and 4 vCPU.
I also did a Explain Extended on the query and below is what it shows. Appreciate any help.
Initially the search of the bizevent_actions didn;t have the indexes defined. I have defined the indexes for them and tried the query but of no use.
One technique that worked for me in a similar situation was abandoning the idea of JOIN completely and switching to queries by PK. More detailed: find out which table in join would give less rows on average if you use only that table and related filter to query; get the primary keys from that table and then query the other one using WHERE pk IN ().
In your case one example would be:
SELECT
bizevent0_.id as id1_37_,
bizevent0_.json as json2_37_,
bizevent0_.account_id as account_3_37_,
...
FROM BizEvent bizevent0_
WHERE
bizevent0_.createdOn >= '1969-12-31 19:00:01.0'
AND bizevent0_.id IN (
SELECT BizEvent_id
FROM BizEvent_actions actions1_
WHERE
actions1_.action <> 'SoftLock'
and actions1_.targetRefClazz = 'com.biznuvo.core.orm.domain.org.EmployeeGroup'
and actions1_.targetRefIdAsStr = '1'
or actions1_.action <> 'SoftLock'
and actions1_.objectRefClazz = 'com.biznuvo.core.orm.domain.org.EmployeeGroup'
and actions1_.objectRefIdAsStr = '1')
ORDER BY
bizevent0_.createdOn;
This assumes that you're not actually willing to select 33+ Mio rows from BizEvent though - your code with LEFT OUTER JOIN would have done exactly this.

MySQL - Fix my poor performing UPDATE?

I have an event which fires periodically to 'abort' some abandoned games (a simple matching server).
This update is proving very (VERY) slow and I'm looking for advice on doing this better.
Problematic Update:
UPDATE user SET skill=skill+
(SELECT count(participant_1) * 25 FROM matches
WHERE score_2 IS NULL
AND score_2_time IS NOT NULL
AND participant_1=user.id
AND score_2_time < (NOW() - INTERVAL 1 HOUR)
AND status=0);
Matches table:
matches CREATE TABLE `matches` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `match_hash` varchar(64) DEFAULT NULL,
 `skill` int(10) unsigned DEFAULT NULL,
 `status` int(10) unsigned DEFAULT NULL,
 `participant_1` int(10) unsigned DEFAULT NULL,
 `score_1` int(10) unsigned DEFAULT NULL,
 `score_1_time` timestamp NULL DEFAULT NULL,
 `participant_1_rematched` tinyint(4) DEFAULT NULL,
 `participant_2` int(10) unsigned DEFAULT NULL,
 `score_2` int(10) unsigned DEFAULT NULL,
 `score_2_time` timestamp NULL DEFAULT NULL,
 `participant_2_rematched` tinyint(4) DEFAULT NULL,
 `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
 `finished_at` timestamp NULL DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=40667 DEFAULT CHARSET=latin1
User table:
user CREATE TABLE `user` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `name` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
 `skill` int(10) unsigned DEFAULT NULL,
 `created` timestamp NOT NULL DEFAULT current_timestamp(),
 PRIMARY KEY (`id`),
 UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1876 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
Any guidance is greatly appreciated.
You need more indexes on the matches table, certainly at least participant_1, and whatever else is mentioned in the WHERE clause that helps. Probably participant_1 should be a foreign key into user.id for integrity reasons.
Try this query:
update user a join (SELECT participant_1,count(participant_1) * 25 as count FROM matches
WHERE score_2 IS NULL
AND score_2_time IS NOT NULL
AND score_2_time < (NOW() - INTERVAL 1 HOUR)
AND status=0 group by participant_1) b on a.id=b.participant_1 SET a.skill=a.skill+b.count

Slow update Query

When i try to update my torrents table (My torrent site permits to share only open source stuff) with the following query
UPDATE `torrents` SET `leech` = '0', `seed` = '1' WHERE `id` = '26784'
It take approximaty 0.5 seconds to update a table which contains only 20,000 records . My other queries are executed in less than 0.0478s (SELECT queries)
CREATE TABLE IF NOT EXISTS `torrents` (
`id` int(11) NOT NULL,
`hash_info` varchar(255) NOT NULL,
`category_slug` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`size` bigint(20) NOT NULL,
`age` int(11) NOT NULL,
`description` text NOT NULL,
`trackers` longtext NOT NULL,
`magnet` longtext,
`files` longtext,
`parent_category` int(11) NOT NULL,
`category` int(11) NOT NULL,
`publish_date` int(11) DEFAULT NULL,
`uploader` int(11) NOT NULL,
`seed` int(11) DEFAULT '0',
`leech` int(11) DEFAULT '0',
`file_key` varchar(255) DEFAULT NULL,
`comments_count` int(11) DEFAULT '0'
) ENGINE=InnoDB AUTO_INCREMENT=26816 DEFAULT CHARSET=latin1;
Any solution ?
Lookups based on the indexed columns are much faster than the lookups on the non-indexed columns. This behavior can be case more visible with the growing amount of the data.
Create an index on Id column and check if it helps you improve the performance of the query.
id is declared to be an integer. So, first your comparison should be to an integer not a string:
UPDATE `torrents`
SET `leech` = '0', `seed` = '1'
WHERE `id` = 26784;
Second, you need an index on the id. You can create one by:
create index idx_torrents_id on torrents(id);
Alternatively, make it a primary key in the table.

Query large table with 50 million rows

trying to query a large table (senddb.order_histories) that has close to 50M rows and this is the MySQL query I am using:
FIRST APPROACH- inner join:
select a.id,
a.order_number,
a.sku_id,
a.fulfillment_status,
a.modified_by,
a.created_at,
a.updated_at
from senddb.order_line_items a
inner join (
select order_line_item_id,
order_number,
order_status,
order_status_description,
action,
modified_by,
created_at,
max(updated_at) as updated_at
from senddb.order_histories
where order_status in ('x','y','z')
and fulfillment_location = 'abcd'
group by order_line_item_id) as b
on a.id = b.order_line_item_id
and a.fulfillment_status = '2';
EXPLAIN output :
SECOND APPROACH- nested select:
select a.id,
a.order_number,
a.sku_id,
a.fulfillment_status,
a.modified_by,
a.created_at,
a.updated_at
from senddb.order_line_items a
where a.fulfillment_status = '2'
and a.id in (
select b.order_line_item_id from(
select order_line_item_id,
order_number,
order_status,
order_status_description,
action,
modified_by,
created_at,
max(updated_at) as updated_at
from senddb.order_histories
where
order_status in ('x','y','z')
and fulfillment_location = 'abcd'
group by order_line_item_id) as b);
I believe nested select is a bad approach on large data but i anyhow added it here because it worked on my sample set. Anyway both the queries eventually time out after 600 seconds with the message : Error Code: 2013. Lost connection to MySQL server during query.
I would like to know if there are any ways to alter the query to make it run faster. I have already tried reducing the columns in the inner select / inner join but that should not really be an issue IMO. I also looked up a solution that says "create a clustered index" but i wasn't really able to follow. Any help is appreciated.
TABLE order_histories :
order_histories CREATE TABLE `order_histories` (
`id` int(4) unsigned NOT NULL AUTO_INCREMENT,
`order_number` varchar(24) DEFAULT NULL,
`order_status_description` varchar(255) DEFAULT NULL,
`datetime_stamp` datetime DEFAULT NULL,
`action` varchar(32) DEFAULT NULL,
`fulfillment_location` int(8) DEFAULT NULL,
`order_status` int(8) DEFAULT NULL,
`user_id` int(8) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`modified_by` varchar(32) DEFAULT NULL,
`order_line_item_id` int(11) DEFAULT NULL,
`pooled` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `order_histories_ecash_idx` (`order_number`),
KEY `order_line_item_id` (`order_line_item_id`)
) ENGINE=InnoDB AUTO_INCREMENT=454738178 DEFAULT CHARSET=latin1
TABLE order_line_items :
order_line_items CREATE TABLE `order_line_items` (
`id` int(4) unsigned NOT NULL AUTO_INCREMENT,
`order_number` varchar(24) DEFAULT NULL,
`sku_id` int(8) DEFAULT NULL,
`original_price` float DEFAULT NULL,
`dept_description` varchar(100) DEFAULT NULL,
`description` varchar(100) DEFAULT NULL,
`quantity_ordered` int(8) DEFAULT NULL,
`gift_indicator` char(1) DEFAULT NULL,
`gift_wrap_flag` char(1) DEFAULT NULL,
`shipping_record_flag` char(1) DEFAULT NULL,
`gift_comments` varchar(100) DEFAULT NULL,
`item_status` char(1) DEFAULT NULL,
`tax_amount` float DEFAULT NULL,
`tax_rate` float DEFAULT NULL,
`upc` varchar(20) DEFAULT NULL,
`final_price` float DEFAULT NULL,
`line_number` int(8) DEFAULT NULL,
`master_line_number` int(8) DEFAULT NULL,
`gift_wrap_flag_type` char(1) DEFAULT NULL,
`color_code` varchar(4) DEFAULT NULL,
`size_id` varchar(6) DEFAULT NULL,
`width_id` varchar(6) DEFAULT NULL,
`brand` varchar(15) DEFAULT NULL,
`vpn` varchar(30) DEFAULT NULL,
`dept_number` int(8) DEFAULT NULL,
`class_number` int(8) DEFAULT NULL,
`non_merch_item` char(1) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`modified_by` varchar(32) DEFAULT NULL,
`chain_id` int(11) DEFAULT NULL,
`fulfillment_location` int(11) DEFAULT NULL,
`fulfillment_date` datetime DEFAULT NULL,
`fulfillment_status` int(11) DEFAULT NULL,
`fulfillment_sales_associate` int(11) DEFAULT NULL,
`gift_wrap_line_number` int(11) DEFAULT NULL,
`shipping_type` int(11) DEFAULT NULL,
`order_track_info_id` int(11) DEFAULT NULL,
`store_tlog_updated` varchar(1) DEFAULT NULL,
`shipping_tlx_code` int(11) DEFAULT NULL,
`store_closed` tinyint(1) DEFAULT NULL,
`flags` int(11) DEFAULT NULL,
`deal_based_index` int(11) DEFAULT NULL,
`tlog_calc_ret_price` float DEFAULT NULL,
`tlog_amount` float DEFAULT NULL,
`tlog_retail_price` float DEFAULT NULL,
`tlog_ext_amount` float DEFAULT NULL,
`tlog_flag_1` int(11) DEFAULT NULL,
`tlog_flag_2` int(11) DEFAULT NULL,
`tlog_flag_3` int(11) DEFAULT NULL,
`time_remaining` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `order_line_items_ecash_idx` (`order_number`),
KEY `order_line_item_fulfillment_location_idx` (`fulfillment_location`),
KEY `order_line_item_fulfillment_status_idx` (`fulfillment_status`),
KEY `upc_idx` (`upc`),
KEY `sku_id_idx` (`sku_id`),
KEY `order_line_items_idx001` (`order_number`,`id`,`fulfillment_status`),
KEY `order_track_info_id` (`order_track_info_id`),
KEY `shipping_type_idx` (`shipping_type`,`non_merch_item`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11367052 DEFAULT CHARSET=latin1
This query can be simplified:
select a.id,
a.order_number,
a.sku_id,
a.fulfillment_status,
a.modified_by,
a.created_at,
a.updated_at
from senddb.order_line_items a
inner join senddb.order_histories b on a.id = b.order_line_item_id
where b.order_status in ('x','y','z')
and b.fulfillment_location = 'abcd'
and a.fulfillment_status = '2';
Since you're only selecting values from table a, you don't need to select specific values from table b and can instead just apply your conditions. Outside of this, you need to ensure that b.order_line_item_id has an index on it. You can find more about creating indexes here. I'm not an expert in MySQL but something similar to this should work if senddb.order_histories.order_line_item_id isn't already the primary key.
CREATE INDEX IX_order_histories_order_line_item_id ON order_histories (order_line_item_id);
You need to read up the optimization section of the MySQL docs. It contains a lot of information on how you can optimize your queries and data sets. The main idea here is to add indexes to the fields that are being used as the criteria in the WHERE clause of the SQL statements.
Basically, both of your alternatives are using a "sub-SELECT, not an INNER JOIN.
The syntax of a true JOIN is one of the following:
SELECT ...
FROM X INNER JOIN Y USING (field_list)
... or ...
SELECT ...
FROM X INNER JOIN Y ON (x.field1 = y.field2) ...
But in both cases the objects being joined are tables or views.
I'm going to presume ... admittedly, without checking ... that Nick Larsen's answer #1 adequately re-expresses your original query using JOINs.
(Notice how, in his answer, the shorthand identifiers A and B are introduced as referring to each of the two table-names mentioned in his query.)
Firstly, you need to decide if a 50 million resultset is what you are asking for. Big data tables are not there so that you can select all their rows. They are there so that you can ask them questions using sql queries. SQL is a query language, it's not a data loading language.
What's your purpose? If you want to copy the data you can do that by loading the data, for example, 1000 rows per query in a for loop. if you are loading the data for processing, you can do that in the same way.
If you want to derive statistical information, you can use outer join and return a low number of rows, using aggregate functions. But you shouldn't do that either, what you "should" do is to decide what you want from the table and preferably, run aggregate functions to store useful information in a different table. (mostly SELECT INTO queries) You should never need to join a table of 50 million records in the first place.
Telling you how to do something wrong using indexes wouldn't be the right thing here.

mysql best practice to concentrate data from multiple tables with different table design

My database contains around 20 tables that holds a user's information. For example it has
Personal : hold user personal info
Documents : uploaded files
Activities :
Etc..
Every table contains a user_Id column for wiring them together ( one to many relationship), along with different table specific columns and constraints.
My question is how should I fetch all data for a single user from all these tables ?
Currently when ever I load user , application need to do
Select * from table1 where user_Id = x;
Select * from table2 where user_Id = x;
Select * from table3 where user_Id = x;
..etc
Since I'm using php (oop) its not a bad thing as every table has its own model that retrieve it. Yet I'm worried about performance as I currently run over 20 queries every time I load page. And since these data are very dynamically updated. Caching isn't helping much.
So what is the best methodology to fix this ?
example of table structures
CREATE TABLE IF NOT EXISTS `documents` (
`id` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`collection` text NOT NULL,
`photo_date` timestamp NULL DEFAULT NULL,
`gallery` varchar(50) NOT NULL,
`cover` int(1) DEFAULT NULL,
`upload_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=8 ;
CREATE TABLE IF NOT EXISTS `problemlist` (
`id` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`visit_id` int(10) unsigned NOT NULL,
`pt_id` int(10) unsigned NOT NULL,
`problem` varchar(200) NOT NULL,
`severity` int(2) unsigned NOT NULL,
`note` text,
`solved` int(1) unsigned NOT NULL,
`datetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;
CREATE TABLE IF NOT EXISTS `visits` (
`id` int(10) unsigned NOT NULL,
`pt_id` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`visit_date` timestamp NULL DEFAULT NULL,
`visit_end` timestamp NULL DEFAULT NULL,
`complain` varchar(250) DEFAULT NULL,
`dx` varchar(200) DEFAULT NULL,
`note` text,
`stats` enum('booked','waitting','finished','noshow','canceled','inroom') NOT NULL,
`deleted` int(1) DEFAULT NULL,
`booked_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`arrived_at` timestamp NULL DEFAULT NULL,
`started_at` timestamp NULL DEFAULT NULL,
`checkout_at` timestamp NULL DEFAULT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=212 ;