优化mysql数据库的几个步骤
1、选取最适用的字段属性,尽可能减少定义字段长度,尽量把字段设置NOT NULL,例如'省份,性别',最好设置为ENUM
2、使用连接(JOIN)来代替子查询:
a删除没有任何订单客户:DELETE FROM customerinfo WHERE customerid NOT in(SELECT customerid FROM orderinfo)
b提取所有没有订单客户:SELECT FROM customerinfo WHERE customerid NOT in(SELECT customerid FROM orderinfo)
c提高b的速度优化:SELECT FROM customerinfo LEFT JOIN orderid customerinfocustomerid=orderinfocustomerid
WHERE orderinfocustomerid IS NULL
3、使用联合(UNION)来代替手动创建的临时表
a创建临时表:SELECT name FROM `nametest` UNION SELECT username FROM `nametest2`
4、事务处理:
a保证数据完整性,例如添加和修改同时,两者成立则都执行,一者失败都失败
mysql_query("BEGIN");
mysql_query("INSERT INTO customerinfo (name) VALUES ('$name1')";
mysql_query("SELECT FROM `orderinfo` where customerid="$id");
mysql_query("COMMIT");
5、锁定表,优化事务处理:
a我们用一个 SELECT 语句取出初始数据,通过一些计算,用 UPDATE 语句将新值更新到表中。
包含有 WRITE 关键字的 LOCK TABLE 语句可以保证在 UNLOCK TABLES 命令被执行之前,
不会有其它的访问来对 inventory 进行插入、更新或者删除的操作
mysql_query("LOCK TABLE customerinfo READ, orderinfo WRITE");
mysql_query("SELECT customerid FROM `customerinfo` where id="$id);
mysql_query("UPDATE `orderinfo` SET ordertitle='$title' where customerid="$id);
mysql_query("UNLOCK TABLES");
6、使用外键,优化锁定表
a把customerinfo里的customerid映射到orderinfo里的customerid,
任何一条没有合法的customerid的记录不会写到orderinfo里
CREATE TABLE customerinfo
(
customerid INT NOT NULL,
PRIMARY KEY(customerid)
)TYPE = INNODB;
CREATE TABLE orderinfo
(
orderid INT NOT NULL,
customerid INT NOT NULL,
PRIMARY KEY(customerid,orderid),
FOREIGN KEY (customerid) REFERENCES customerinfo
(customerid) ON DELETE CASCADE
)TYPE = INNODB;
注意:'ON DELETE CASCADE',该参数保证当customerinfo表中的一条记录删除的话同时也会删除order
表中的该用户的所有记录,注意使用外键要定义事务安全类型为INNODB;
7、建立索引:
a格式:
(普通索引)->
创建:CREATE INDEX <索引名> ON tablename (索引字段)
修改:ALTER TABLE tablename ADD INDEX [索引名] (索引字段)
创表指定索引:CREATE TABLE tablename([],INDEX[索引名](索引字段))
(唯一索引)->
创建:CREATE UNIQUE <索引名> ON tablename (索引字段)
修改:ALTER TABLE tablename ADD UNIQUE [索引名] (索引字段)
创表指定索引:CREATE TABLE tablename([],UNIQUE[索引名](索引字段))
(主键)->
它是唯一索引,一般在创建表是建立,格式为:
CREATA TABLE tablename ([],PRIMARY KEY[索引字段])
8、优化查询语句
a最好在相同字段进行比较操作,在建立好的索引字段上尽量减少函数操作
例子1:
SELECT FROM order WHERE YEAR(orderDate)<2008;(慢)
SELECT FROM order WHERE orderDate<"2008-01-01";(快)
例子2:
SELECT FROM order WHERE addtime/7<24;(慢)
SELECT FROM order WHERE addtime<247;(快)
例子3:
SELECT FROM order WHERE title like "%good%";
SELECT FROM order WHERE title>="good" and name<"good";
数据库优化一方面是找出系统的瓶颈,提高MySQL数据库的整体性能,而另一方面需要合理的结构设计和参数调整,以提高用户的相应速度,同时还要尽可能的节约系统资源,以便让系统提供更大的负荷
1 优化一览图
2 优化
笔者将优化分为了两大类,软优化和硬优化,软优化一般是操作数据库即可,而硬优化则是操作服务器硬件及参数设置
21 软优化
211 查询语句优化
1首先我们可以用EXPLAIN或DESCRIBE(简写:DESC)命令分析一条查询语句的执行信息
2例:
显示:
其中会显示索引和查询数据读取数据条数等信息
212 优化子查询
在MySQL中,尽量使用JOIN来代替子查询因为子查询需要嵌套查询,嵌套查询时会建立一张临时表,临时表的建立和删除都会有较大的系统开销,而连接查询不会创建临时表,因此效率比嵌套子查询高
213 使用索引
索引是提高数据库查询速度最重要的方法之一,关于索引可以参高笔者<MySQL数据库索引>一文,介绍比较详细,此处记录使用索引的三大注意事项:
214 分解表
对于字段较多的表,如果某些字段使用频率较低,此时应当,将其分离出来从而形成新的表,
215 中间表
对于将大量连接查询的表可以创建中间表,从而减少在查询时造成的连接耗时
216 增加冗余字段
类似于创建中间表,增加冗余也是为了减少连接查询
217 分析表,,检查表,优化表
分析表主要是分析表中关键字的分布,检查表主要是检查表中是否存在错误,优化表主要是消除删除或更新造成的表空间浪费
1 分析表: 使用 ANALYZE 关键字,如ANALYZE TABLE user;
2 检查表: 使用 CHECK关键字,如CHECK TABLE user [option]
option 只对MyISAM有效,共五个参数值:
3 优化表:使用OPTIMIZE关键字,如OPTIMIZE [LOCAL|NO_WRITE_TO_BINLOG] TABLE user;
LOCAL|NO_WRITE_TO_BINLOG都是表示不写入日志,优化表只对VARCHAR,BLOB和TEXT有效,通过OPTIMIZE TABLE语句可以消除文件碎片,在执行过程中会加上只读锁
22 硬优化
221 硬件三件套
1配置多核心和频率高的cpu,多核心可以执行多个线程
2配置大内存,提高内存,即可提高缓存区容量,因此能减少磁盘I/O时间,从而提高响应速度
3配置高速磁盘或合理分布磁盘:高速磁盘提高I/O,分布磁盘能提高并行操作的能力
222 优化数据库参数
优化数据库参数可以提高资源利用率,从而提高MySQL服务器性能MySQL服务的配置参数都在mycnf或myini,下面列出性能影响较大的几个参数
223 分库分表
因为数据库压力过大,首先一个问题就是高峰期系统性能可能会降低,因为数据库负载过高对性能会有影响。另外一个,压力过大把你的数据库给搞挂了怎么办?所以此时你必须得对系统做分库分表 + 读写分离,也就是把一个库拆分为多个库,部署在多个数据库服务上,这时作为主库承载写入请求。然后每个主库都挂载至少一个从库,由从库来承载读请求。
224 缓存集群
如果用户量越来越大,此时你可以不停的加机器,比如说系统层面不停加机器,就可以承载更高的并发请求。然后数据库层面如果写入并发越来越高,就扩容加数据库服务器,通过分库分表是可以支持扩容机器的,如果数据库层面的读并发越来越高,就扩容加更多的从库。但是这里有一个很大的问题:数据库其实本身不是用来承载高并发请求的,所以通常来说,数据库单机每秒承载的并发就在几千的数量级,而且数据库使用的机器都是比较高配置,比较昂贵的机器,成本很高。如果你就是简单的不停的加机器,其实是不对的。所以在高并发架构里通常都有缓存这个环节,缓存系统的设计就是为了承载高并发而生。所以单机承载的并发量都在每秒几万,甚至每秒数十万,对高并发的承载能力比数据库系统要高出一到两个数量级。所以你完全可以根据系统的业务特性,对那种写少读多的请求,引入缓存集群。具体来说,就是在写数据库的时候同时写一份数据到缓存集群里,然后用缓存集群来承载大部分的读请求。这样的话,通过缓存集群,就可以用更少的机器资源承载更高的并发。
一个完整而复杂的高并发系统架构中,一定会包含:各种复杂的自研基础架构系统。各种精妙的架构设计因此一篇小文顶多具有抛砖引玉的效果,但是数据库优化的思想差不多就这些了
(1)系统服务优化,把MySQL的key_buffer、cache_buffer、query_cache等增加容量
(2)给所有经常查询的字段增加适当的索引
(3)优化SQL语句,减少Ditinct、Group、Join等等语句的操作
一个数据库服务器高iowait的优化案例
1开发反馈某一测试环境sql运行缓慢,而在其他测试环境该sql运行很快。两个环境其配置相同,均只部署了mysql服务器。
2执行top命令发现sql运行缓慢的机器上磁盘iowait较sql运行较快的机器高出很多。推测这是导致sql运行缓慢的主因,因为该sql是要读取表,表较大,且要扫描的行数较多。
3到底是什么导致机器iowait高呢,执行iotop发现消耗io的进程主要是mysql,而且主要是mysql上的读操作。
4想必是有其他高频运行的查询语句不停从某大表中查询数据,且查询时可能使用不到索引,要扫描的表行数较多,从而导致高频io操作,致使其他需io操作的sql运行缓慢。
5究竟是什么sql引起的呢?开启了general log,发现收集到的语句太多,且不能很好定位到高开销的sql。
6开启slow log,long_query_time置为1,来捕获慢查询,同时使用pt-ioprofile用来追踪mysql数据文件中哪些文件上的io消耗比较多。
7综合slow log(可使用pt-query-digest进行聚合)和pt-ioprofile的结果发现确实是两条典型的需要扫描全表的且对应的表非常大的sql频繁执行导致了磁盘的高io。
8那么剩下的问题便是优化表或者查询了。最简单直接的是通过建合适的索引来提升查询性能,减少表扫描行数,需要继续榨取性能的话就是优化sql的写法,调整表结构,调整参数配置来解决了。
9先从收益最大的方法入手,先评估sql语句,根据语句中的条件查看各个字段的数据分布情况,通过explain等评估在字段上创建索引或多列联合索引的合理性,并创建合适的索引。
10最后发现建好索引后原来需要扫全表的语句通过索引可有效减少扫描行数,继而io操作减少了,服务器的iowait讲题,原来反馈的运行较慢的sql运行速度得以提升,但还是不够理想。
11最后通过在该慢语句对应的表建索引,并修正where条件中使用错误的值类型极大的提升了sql语句运行速度,且服务器整体IO消耗大大降低。
12可通过pt-query-digest聚合优化后mysql server产生的slow log以及使用pt-ioprofile分析优化后mysql数据文件io占用情况,可了解到优化前后的差异情况。
1、选取最适用的字段属性
MySQL 可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。同样的,如果可以的话,我们应该使用MEDIUMINT而不是BIGIN来定义整型字段。
另外一个提高效率的方法是在可能的情况下,应该尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。
2、使用连接(JOIN)来代替子查询(Sub-Queries)
MySQL 从41开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。例如,我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户ID取出来,然后将结果传递给主查询,如下所示:
DELETE FROM customerinfo
WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )
使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询可以被更有效率的连接(JOIN) 替代。例如,假设我们要将所有没有订单记录的用户取出来,可以用下面这个查询完成:
SELECT FROM customerinfo
WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )
如果使用连接(JOIN) 来完成这个查询工作,速度将会快很多。尤其是当salesinfo表中对CustomerID建有索引的话,性能将会更好,查询如下:
SELECT FROM customerinfo
LEFT JOIN salesinfoON customerinfoCustomerID=salesinfo
CustomerID
WHERE salesinfoCustomerID IS NULL
连接(JOIN) 之所以更有效率一些,是因为 MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。
3、使用联合(UNION)来代替手动创建的临时表
MySQL 从 40 的版本开始支持 UNION 查询,它可以把需要使用临时表的两条或更多的 SELECT 查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用 UNION 来创建查询的时候,我们只需要用 UNION作为关键字把多个 SELECT 语句连接起来就可以了,要注意的是所有 SELECT 语句中的字段数目要想同。下面的例子就演示了一个使用 UNION的查询。
SELECT Name, Phone FROM client
UNION
SELECT Name, BirthDate FROM author
UNION
SELECT Name, Supplier FROM product
4、事务
尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条SQL语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。设想一下,要把某个数据同时插入两个相关联的表中,可能会出现这样的情况:第一个表中成功更新后,数据库突然出现意外状况,造成第二个表中的操作没有完成,这样,就会造成数据的不完整,甚至会破坏数据库中的数据。要避免这种情况,就应该使用事务,它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN 关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态。
BEGIN;
INSERT INTO salesinfo SET CustomerID=14;
UPDATE inventory SET Quantity=11
WHERE item='book';
COMMIT;
事务的另一个重要作用是当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方法来为用户提供一种安全的访问方式,这样可以保证用户的操作不被其它的用户所干扰。
5、锁定表
尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是在很大的应用系统中。由于在事务执行的过程中,数据库将会被锁定,因此其它的用户请求只能暂时等待直到该事务结束。如果一个数据库系统只有少数几个用户
来使用,事务造成的影响不会成为一个太大的问题;但假设有成千上万的用户同时访问一个数据库系统,例如访问一个电子商务网站,就会产生比较严重的响应延迟。
其实,有些情况下我们可以通过锁定表的方法来获得更好的性能。下面的例子就用锁定表的方法来完成前面一个例子中事务的功能。
LOCK TABLE inventory WRITE
SELECT Quantity FROM inventory
WHEREItem='book';
UPDATE inventory SET Quantity=11
WHEREItem='book';
UNLOCK TABLES
这里,我们用一个 SELECT 语句取出初始数据,通过一些计算,用 UPDATE 语句将新值更新到表中。包含有 WRITE 关键字的 LOCK TABLE 语句可以保证在 UNLOCK TABLES 命令被执行之前,不会有其它的访问来对 inventory 进行插入、更新或者删除的操作。
6、使用外键
锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。例如,外键可以保证每一条销售记录都指向某一个存在的客户。在这里,外键可以把customerinfo 表中的CustomerID映射到salesinfo表中CustomerID,任何一条没有合法CustomerID的记录都不会被更新或插入到 salesinfo中。
CREATE TABLE customerinfo
(
CustomerID INT NOT NULL ,
PRIMARY KEY ( CustomerID )
) TYPE = INNODB;
CREATE TABLE salesinfo
(
SalesID INT NOT NULL,
CustomerID INT NOT NULL,
PRIMARY KEY(CustomerID, SalesID),
FOREIGN KEY (CustomerID) REFERENCES customerinfo
(CustomerID) ON DELETECASCADE
) TYPE = INNODB;
注意例子中的参数“ON DELETE CASCADE”。该参数保证当 customerinfo 表中的一条客户记录被删除的时候,salesinfo 表中所有与该客户相关的记录也会被自动删除。如果要在 MySQL 中使用外键,一定要记住在创建表的时候将表的类型定义为事务安全表 InnoDB类型。该类型不是 MySQL 表的默认类型。定义的方法是在 CREATE TABLE 语句中加上 TYPE=INNODB。如例中所示。
7、使用索引
索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(), MIN()和ORDERBY这些命令的时候,性能提高更为明显。那该对哪些字段建立索引呢?一般说来,索引应建立在那些将用于JOIN, WHERE判断和ORDER BY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。对于一个ENUM类型的字段来说,出现大量重复值是很有可能的情况,例如 customerinfo中的“province” 字段,在这样的字段上建立索引将不会有什么帮助;相反,还有可能降低数据库的性能。我们在创建表的时候可以同时创建合适的索引,也可以使用ALTER TABLE或CREATE INDEX在以后创建索引。此外,MySQL
从版本32323开始支持全文索引和搜索。全文索引在 MySQL 中是一个FULLTEXT类型索引,但仅能用于MyISAM 类型的表。对于一个大的数据库,将数据装载到一个没有FULLTEXT索引的表中,然后再使用ALTER TABLE或CREATE INDEX创建索引,将是非常快的。但如果将数据装载到一个已经有FULLTEXT索引的表中,执行过程将会非常慢。
8、优化的查询语句
绝大多数情况下,使用索引可以提高查询的速度,但如果SQL语句使用不恰当的话,索引将无法发挥它应有的作用。下面是应该注意的几个方面。首先,最好是在相同类型的字段间进行比较的操作。在MySQL 323版之前,这甚至是一个必须的条件。例如不能将一个建有索引的INT字段和BIGINT字段进行比较;但是作为特殊的情况,在CHAR类型的字段和 VARCHAR类型字段的字段大小相同的时候,可以将它们进行比较。其次,在建有索引的字段上尽量不要使用函数进行操作。
例如,在一个DATE类型的字段上使用YEAE()函数时,将会使索引不能发挥应有的作用。所以,下面的两个查询虽然返回的结果一样,但后者要比前者快得多。
SELECT FROM order WHERE YEAR(OrderDate)<2001;
SELECT FROM order WHERE OrderDate<"2001-01-01";
同样的情形也会发生在对数值型字段进行计算的时候:
SELECT FROM inventory WHERE Amount/7<24;
SELECT FROM inventory WHERE Amount<247;
上面的两个查询也是返回相同的结果,但后面的查询将比前面的一个快很多。第三,在搜索字符型字段时,我们有时会使用 LIKE 关键字和通配符,这种做法虽然简单,但却也是以牺牲系统性能为代价的。例如下面的查询将会比较表中的每一条记录。
SELECT FROM books
WHERE name like "MySQL%"
但是如果换用下面的查询,返回的结果一样,但速度就要快上很多:
SELECT FROM books
WHERE name>="MySQL"and name<"MySQM"
最后,应该注意避免在查询中让MySQL进行自动类型转换,因为转换过程也会使索引变得不起作用。
查询指定的记录最好通过Id进行in查询来获得真实的数据其实不是最好而是必须,也就是你应该先查询出复合的ID列表,通过in查询来获得数据
我们来做一个测试ipdatas表:
CREATE TABLE `ipdatas` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`uid` INT(8) NOT NULL DEFAULT '0',
`ipaddress` VARCHAR(50) NOT NULL,
`source` VARCHAR(255) DEFAULT NULL,
`track` VARCHAR(255) DEFAULT NULL,
`entrance` VARCHAR(255) DEFAULT NULL,
`createdtime` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
`createddate` DATE NOT NULL DEFAULT '0000-00-00',
PRIMARY KEY (`id`),
KEY `uid` (`uid`)
) ENGINE=MYISAM AUTO_INCREMENT=67086110 DEFAULT CHARSET=utf8;
这是我们做的广告联盟的推广ip数据记录表,由于我也不是mysql的DBA所以这里咱们仅仅是测试
因为原来里面有大概7015291条数据
这里我们通过jdbc的batch插入6000万条数据到此表当中“JDBC插入6000W条数据用时:9999297ms”;
大概用了两个多小时,这里面我用的是batch大小大概在1w多每次提交,还有一点是每次提交的数据都很小,而且这里用的myisam数据表,因为我需要知道mysql数据库的大小以及索引数据的大小结果是
ipdatasMYD 399 GB (4,288,979,008 字节)
ipdatasMYI 128 GB (1,377,600,512 字节)
这里面我要说的是如果真的是大数据如果时间需要索引还是最好改成数字字段,索引的大小和查询速度都比时间字段可观。
步入正题:
1全表搜索
返回结构是67015297条数据
SELECT COUNT(id) FROM ipdatas;
SELECT COUNT(uid) FROM ipdatas;
SELECT COUNT() FROM ipdatas;
首先这两个全表数据查询速度很快,mysql中包含数据字典应该保留了数据库中的最大条数
查询索引条件
SELECT COUNT() FROM ipdatas WHERE uid=1; 返回结果时间:2分31秒594
SELECT COUNT(id) FROM ipdatas WHERE uid=1; 返回结果时间:1分29秒609
SELECT COUNT(uid) FROM ipdatas WHERE uid=1; 返回结果时间:2分41秒813
第二次查询都比较快因为mysql中是有缓存区的所以增大缓存区的大小可以解决很多查询的优化,真可谓缓存无处不在啊在程序开发中也是层层都是缓存
查询数据
第一条开始查询
SELECT FROM ipdatas ORDER BY id DESC LIMIT 1,10 ; 31毫秒
SELECT FROM ipdatas LIMIT 1,10 ; 15ms
在JAVA开发中数据库的学习也是我们需要了解的,截下来几篇文章都是关于数据库的设计和应用,那么java课程培训机构废话不多说开始学习吧!
数据库的设计
数据库设计是基础,数据库优化是建立在设计基础之上的。好的数据库一定拥有好的设计。
数据库设计的目标是为用户和各种应用系统提供一个信息基础设施和高效的运行环境。
数据库的三大范式
第一范式1NF:所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。
第二范式2Nf:第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
第三范式3Nf:所有字段必须与主键直接相关,而不是间接相关。也可以理解为字段不要和其他非主键字段相关
注意:这三个范式尽可能去遵守,不是一定要墨守成规这只是让我们设计的表的时候,越靠近这些范式,可以使字段尽量的减小冗余但是有时候也可以根据实际需要小小的违背一下但是第三范式违反一下还可以接受,但是第一范式别违反
数据库设计的步骤
需求分析阶段
准确了解与分析用户需求(包括数据与处理)。是整个设计过程的基础,是最困难、最耗费时间的一步。
概念结构设计阶段
是整个数据库设计的关键--设计数据库的E-R模型图,确认需求信息的正确和完整
Entity_Relationship---实体之间的关系
一对一
一对多
多对一
0条评论