转:mysql用户与权限

MYSQL用户与权限

http://www.cublog.cn/u3/118426/showart_2344972.html
 
在MYSQL中,你可以根据自己的需要定制不同的用户。比如,定制来自某些IP地址的用户可以访问,并且给这些
用户设置密码。搭配权限的设置,你还可以进一步限定用户的权限范围,比如说仅仅让一个用户只能访问某一个库,并且只能对这个数据库拥有insert和
select权限。
1.添加一个mysql用户
有两种方式来添加mysql用户:
①使用grant语句
②使用mysql授权表

实上,MYSQL中所有关于用户的信息都保存在名字为mysql的这个库中对应的表当中。使用GRANT语句添加用户,实际上是把你在语句中设定的内容写
入mysql库中对应的表里面。因此,直接操作mysql库中对应的表也可以添加用户。不过推荐大家使用GRANT语句来创建新用户,因为这样更加方便。
使用grant语句添加用户
mysql> GRANT ALL PRIVILEGES ON *.* TO 

‘aaa’@’192.168.0.254’


    -> IDENTIFIED BY ‘123456’ WITH GRANT OPTION;
其中,ALL PRIVILEGES 就是所有权限的意思
      ON 语句指定这些权限应用于哪些库和表。*.*代表的是所有的库的所有表
      TO  确定了这些权限给与哪个用户,这里我们把权限赋予给

‘aaa’@’192.168.0.254’

这个用户,aaa是用户名,@符号后面确定了只允许该用户通过192.168.0.254这个ip地址访问。
      IDENTIFIED BY 语句是设定用户密码,在这里用户密码设定为123456
      WITH GRANT OPTION 的意思是允许aaa这个用户把它拥有的权限再赋予其他用户
2.删除一个用户
DROP USER语句用于删除一个或多个MySQL账户。要使用DROP USER,您必须拥有mysql数据库的全局CREATE USER权限或DELETE权限。
例:
mysql> DROP USER
aaa@192.168.0.254

;
3.mysql用户权限管理
刚才,我们使用了GRANT语句来创建用户。实际上,GRANT语句本身是具有双重用途的。当GRANT语句中指定的用户不存在时,将创建该用户并赋予权限;当GRANT语句中指
定的用户存在时,仅仅赋予该用户指定的权限。
在MYSQL中可以赋予用户很多种权限。通过刚才的例子,大家应该知道:想要赋予一
个用户权利,只要在GRANT语句的后面跟上权限就可以了。在下面的表格中列出了MYSQL
中可以为用户设定的权限和它们的含义:

mysql>grant all on bookshop.* to
aaa@192.168.0.254

;//定义了aaa用户只可以对bookshop有操作的权利
如果只允许aaa用户对bookshop数据库只能查看和添加,其他的什么也不能干,命令如下
mysql>grant select,insert on bookshop.* to
aaa@192.168.0.254

在赋予权限之后,不要忘记执行一下FLUSH PRIVILEGES命令刷新权限缓存,来让你的设置立即生效。
mysql> FLUSH PRIVILEGES;
如果你想对某个用户收回某些权限的话,就要用revoke语句,revoke语句和grant语句类似,例如收回aaa用户的select和insert权限
mysql> revoke select,insert on bookshop.* from
aaa@192.168.0.254

;
4.修改用户密码
修改用户密码的方法有很多种,首先介绍用grant后面跟identified by 语句后面写入新密码,例如:
mysql> grant usage on bookshop.* to
aaa@192.168.0.254

identified by ‘654321’;
//这里我们给了aaa一个usage权限,usage的意思就是没权限,然后这样我们就仅仅更该了aaa的密码,没有覆盖权限。
另一个方法是使用 update user 语句,使用这条语句必须切换到mysql库中,因为这条语句实际上就是更新库中的user表内的数据。例如,同样将aaa密码修改成654321
mysql> use mysql;
Database changed
mysql> update user set password=password(‘654321’) where user=”aaa”;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0

5.忘记root密码怎么办 
如果某些普通用户忘记了自己的密码的话,应对的方法很简单,给他重新设置一个密码就好了。可是如果你不小心忘记了root用户的密码,那真的是非常的糟糕。因为只有root用户自己才有权利更改root用户密码的权利。那么该怎么办呢?
首先你需要停掉mysql服务:
#service mysqld stop
或者KILL掉mysql进程
#killall -term mysql
接下来使用mysqld_safe这个命令加上—skip-grant-tables来重新启动mysql。
特别需要提醒你的是,这样启动mysql将允许任何人以root用户和空密码访问mysql服务器!因此,如果你不能断开网络连接的话,那么接下来的所有操作,你必须尽可能的快的去完成。
# mysqld_safe –skip-grant-tables &
# mysql
mysql> use msyql;
mysql> update user set password=password( ‘new_pass’) where user=”root”;
mysql> exit
# service mysqld restart

Web2.0动态网站开发-PHP技术与应用(PDF电子书)

Web2.0动态网站开发PHP技术应用(PDF电子书)
电子书相关:
本书以Web 2.0网站开发的5种需求和28个重要元素为线索,从PHP开发Web 2.0网站的三个理由出发,全面讲解PHP的Web开发技术,并按行业流行的开发模式创建10大Web 2.0应用系统,帮助读者真正实现从新手高手的跨越。
  全书分3部分共21章,第1部分(第1~3章)介绍Web 2.0的概念、应用、重要元素及开发技术;第2部分(第4~11章)结合92个范例讲解PHP的Web开发技术,内容包括PHP开发入门、PHP基本语法、PHP流程控制、PHP函数和类、MySQL、PHP+MySQL数据库开发、PHP动态网站构建全过程、PHP开发中的常用技巧等;第3部分(第12~21章)为实例篇,介绍博客网站系统、基于~ax的留言板、新闻发布系统、为网站添加RSS功能、Wiki网站系统、网络论坛系统、网络邮件系统、网络校友录系统、网络聊天室、网站访问统计等10个系统的设计与实现。
电子书目录
第1部分 Web 2.0大航海时代
——第1章 Web 2.0概念与图谱
——第2章 Web 2.0元素详解
——第3章 Web 2.0开发技术PHP
第2部分 PHP技术之旅
——第4章 PHP开发入门
——第5章 PHP基本语法
——第6章 PHP流程控制
——第7章 PHP函数和类
——第8章 PHP最佳搭档MySQL
——第9章 PHP+MySQL数据库系统开发
——第10章 PHP动态网站构建全程实录
——第11章 PHP开发中的常用技巧
第3部分 Web 2.0大航海之旅
——第12章 博客网站系统
——第13章 基于Ajax的留言板
——第14章 新闻发布系统
——第15章 RSS生成与阅读器制作
——第16章 Wiki网站系统
——第17章 网络论坛系统
——第18章 网络邮件系统
——第19章 网络校友录系统
——第20章 网络聊天室
——第21章 网站访问统计
附录A Ajax框架汇总
附录B 中国互联网Web 2.0百强企业
PS:本贴提供随书光盘下载,包含10大web2.0应用系统的源代码,可以直接部署。

电子书下载:
==============================
[url=Web.2.0动态网站开发——PHP技术与应用.pdf (243.34 MB)]Web2.0动态网站开发-PHP技术与应用(PDF电子书)[/url]
[url=Web.2.0动态网站开发——PHP技术与应用-随书光盘.iso (6.08 MB)]Web2.0动态网站开发-PHP技术与应用(随书光盘)
[/url]
(右键迅雷或电驴下载)

==============================
原帖地址http://www.phpjava.org/thread-409-1-1.html
本文来自: PJDN–php&Java论坛|技术交流社区,打造中国php&java开发者社区[www.phpjava.org]

MySql常用命令总结

1:使用SHOW语句找出在服务器上当前存在什么数据库:
  mysql> SHOW DATABASES;
2:2、创建一个数据库MYSQLDATA
  mysql> CREATE DATABASE MYSQLDATA;
3:选择你所创建的数据库
  mysql> USE MYSQLDATA; (按回车键出现Database changed 时说明操作成功!)
4:查看现在的数据库中存在什么表
  mysql> SHOW TABLES;
5:创建一个数据库表
  mysql> CREATE TABLE MYTABLE (name VARCHAR(20), sex CHAR(1));
6:显示表的结构:
  mysql> DESCRIBE MYTABLE;
7:往表中加入记录
  mysql> insert into MYTABLE values (“hyq”,”M”);
8:用文本方式将数据装入数据库表中(例如D:/mysql.txt)
  mysql> LOAD DATA LOCAL INFILE “D:/mysql.txt” INTO TABLE MYTABLE;
9:导入.sql文件命令(例如D:/mysql.sql)
  mysql>use database;
  mysql>source d:/mysql.sql;
10:删除表
  mysql>drop TABLE MYTABLE;
11:清空表
  mysql>delete from MYTABLE;
12:更新表中数据
  mysql>update MYTABLE set sex=”f” where name=’hyq’;
 

显示MySQL的建表语句
show create table tablename;

获取表的字段信息
show columns from tablename;

 
以下是无意中在网络看到的使用MySql的管理心得,
摘自:http://www1.xjtusky.com/article/htmldata/2004_12/3/57/article_1060_1.html 
  
在windows中MySql以服务形式存在,在使用前应确保此服务已经启动,未启动可用net start mysql命令启动。而Linux中启动时可用“/etc/rc.d/init.d/mysqld start”命令,注意启动者应具有管理员权限。
刚安装好的MySql包含一个含空密码的root帐户和一个匿名帐户,这是很大的安全隐患,对于一些重要的应用我们应将安全性尽可能提高,在这里应把匿名帐户删除、 root帐户设置密码,可用如下命令进行:
use mysql;
delete from User where User=””;
update User set Password=PASSWORD(‘newpassword’) where User=’root’;
如果要对用户所用的登录终端进行限制,可以更新User表中相应用户的Host字段,在进行了以上更改后应重新启动数据库服务,此时登录时可用如下类似命令:
mysql -uroot -p;
mysql -uroot -pnewpassword;
mysql mydb -uroot -p;
mysql mydb -uroot -pnewpassword;
上面命令参数是常用参数的一部分,详细情况可参考文档。此处的mydb是要登录的数据库的名称。
在进行开发和实际应用中,用户不应该只用root用户进行连接数据库,虽然使用root用户进行测试时很方便,但会给系统带来重大安全隐患,也不利于管理技术的提高。我们给一个应用中使用的用户赋予最恰当的数据库权限。如一个只进行数据插入的用户不应赋予其删除数据的权限。MySql的用户管理是通过 User表来实现的,添加新用户常用的方法有两个,一是在User表插入相应的数据行,同时设置相应的权限;二是通过GRANT命令创建具有某种权限的用户。其中GRANT的常用用法如下:
grant all on mydb.* to NewUserName@HostName identified by “password” ;
grant usage on *.* to NewUserName@HostName identified by “password”;
grant select,insert,update on mydb.* to NewUserName@HostName identified by “password”;
grant update,delete on mydb.TestTable to NewUserName@HostName identified by “password”;
若要给此用户赋予他在相应对象上的权限的管理能力,可在GRANT后面添加WITH GRANT OPTION选项。而对于用插入User表添加的用户,Password字段应用PASSWORD 函数进行更新加密,以防不轨之人窃看密码。对于那些已经不用的用户应给予清除,权限过界的用户应及时回收权限,回收权限可以通过更新User表相应字段,也可以使用REVOKE操作。
下面给出本人从其它资料(www.cn-java.com)获得的对常用权限的解释:
全局管理权限:
FILE: 在MySQL服务器上读写文件。
PROCESS: 显示或杀死属于其它用户的服务线程。
RELOAD: 重载访问控制表,刷新日志等。
SHUTDOWN: 关闭MySQL服务。
数据库/数据表/数据列权限:
ALTER: 修改已存在的数据表(例如增加/删除列)和索引。
CREATE: 建立新的数据库或数据表。
DELETE: 删除表的记录。
DROP: 删除数据表或数据库。
INDEX: 建立或删除索引。
INSERT: 增加表的记录。
SELECT: 显示/搜索表的记录。
UPDATE: 修改表中已存在的记录。
特别的权限:
ALL: 允许做任何事(和root一样)。
USAGE: 只允许登录–其它什么也不允许做。

DesignPattern个人读书笔记

                Design  Pattern  个人读书笔记     (2006-12)
要谈Design Pattern就得从复用说起。我们每个人不管是使用经验,公式,工具大都从使用别人已经设计,发现了的东西开始的,而这便是复用的在我们生活中的具体表现。在软件开发中Design Pattern是为便于将来复用(直接或稍作改变使用)前人的成功设计而被提炼出的再经验(从经验中发现共有的部分后进行分类定义)。
Design Pattern这个名词最早是从Christopher Alexander的建筑学名著《A Pattern Language》中引入的。正如Christopher Alexander所说:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。正是基于这样的目的GOF在其书中便是以面向对象设计为背景总结了23个Design Pattern。其各模式组织模板为:模式名和分类,意图,别名,动机,适用性,结构,参与者,协作,效果,实现,代码示例,已知应用,相关模式。
我是从GOF的书开始看的。因为其是以面向对象设计为背景的。故很有必要对面向对象中几个基本概念作一说明。
类:定义了一类活动对象的行为,属性的模板。
对象:封装了状态和行为的静态实体。
接口:仅定义需具体实现的规则集合,以便外部直接使用而屏蔽其具体实现部分从而最终实现了使用与实现的分离。
抽象类:为其子类定义公共接口,而将它的部分或全部的实现延迟到子类。不可实例化。
动态绑定:对象发送的请求所引起的具体操作既与请求本身有关又与接受对象有关,从而支持相同请求的不同对象可能对请求激发的操作有不同的实现。发送给对象的请求和它的相应操作直到运行时才连接起来。
多态:允许在运行时刻彼此替换有相同接口的对象,这种可替换性称为多态性。
继承(范化):类之间为共享部分操作而实现的一种“is a kind of”关系。
组合:类之间为共享部分操作而实现的一种“use a/ has a”关系。(可具体分:关联/聚集“has a”,依赖“use a”)。
在正式进入主题前,还有必要提几条使用面向对象设计的重要原则:
① 针对接口编程,而不是针对实现编程。(尽量使用接口而非实际对象)
② 优先使用对象组合,而非类继承。(依赖于对象组合的设计有更好的复用性)
③ 接口优先于抽象类。(接口使我们可以构造出非层次结构的类型框架)
④ 尽量做到高内聚,低耦合。
⑤ 设计应支持变化,而我常常会遇到类似如下的问题而不得不重新设计。
1. 通过显式地指定一个类来创建对象。
2. 对特殊操作的依赖。
3. 对硬件和软件平台依赖。
4. 对对象表示或实现依赖。
5. 算法依赖。
6. 紧耦合。
7. 通过生成子类来扩充功能。
8. 不能方便的对类进行修改。
现在已经出现了许多应用设计模式的工具(Eclipse,等 ),语言(Smalltalk, Java, C++STL, Python, Ruby等),框架(Structs, Spring, MFC等 )。而即使是C这样的结构化语言,其中的函数指针,变参函数,结构体,宏定义等,某种程度上也有设计模式的影子。

  下面我具体针对几个非常常见的设计模式使用实例进行探讨。
模式的基本概念:即增加一个抽象层。无论什么时候,当你想把某些东西抽象出来的时候,实际上你是在分离特定的细节,这么做的一个有说服力的动机就是把变化的东西从那些不变的东西里分离出来。 
    比喻:就是具体事情做得越多,越容易范错误.这每个做过具体工作的人都深有体会,相反,官做得越高,说出的话越抽象越笼统,犯错误可能性就越少。

抽象工厂 (Abstract Factory):

图略

在GOF书中是由支持多种视感(Look-and-feel)标准的用户界面工具包引入的。

以上设计可使得通过往Player上增加游戏角色,往Obstacle上增加角色行为。只需再实现Player,Obstacle的新成员而不需要改变原来其他不相关代码。也可在GameElementFactory下任意组合新的游戏角色,角色行为组合成新的游戏类型。另外,即使GameEnviroment出问题换一个新的只要实现了相应接口仍可在原系统中使用。
抽象工厂最大的优势便在于可用于系列产品开发。

工厂方法 (Factory Method):

图略

另外,有必要介绍一下Java中的一种动态创建对象方法-Reflect机制:
软件包 java.lang.reflect 的描述(获取关于类和对象的反射信息。在安全限制内,反射允许编程访问关于加载类的字段、方法和构造方法的信息,并允许使用反射字段、方法和构造方法对对象上的基本对等项进行操作。)
Class c = Class.forName(className);  //由类名创建Class对象
Method[] m=c.getMethods();  //由Class对象获得类中的方法
//可使用Method中的invoke来调用具体方法。
// public Object invoke(Object obj,Object… args)Throws IllegalAccessException,
IllegalArgumentException,InvocationTargetException
// obj – 从中调用基础方法的对象 , args – 用于方法调用的参数 
Constructor[] ctor=c.getConstructors();  //由Class对象获得类中的构造函数
factory = (ForumFactory)c.newInstance();  //由Class对象实际创建特型对象
实例如下:(以Jive 的ForumFactory 为例)
public abstract class ForumFactory {
private static Object initLock = new Object();
private static String className =”com.jivesoftware.forum.database.DbForumFactory”;
private static ForumFactory factory = null;
public static ForumFactory getInstance(Authorization authorization) {
//If no valid authorization passed in, return null.
if (authorization == null) {   return null;  }
//以下使用了Singleton 单态模式
if (factory == null) {
synchronized(initLock) {
if (factory == null) {
……
try {//动态转载类  
Class c = Class.forName(className);  factory = (ForumFactory)c.newInstance();
}catch (Exception e) {   return null;   }  }  }   }
//Now, 返回 proxy.用来限制授权对forum 的访问
return new ForumFactoryProxy(authorization, factory,factory.getPermissions(authorization));
}
//真正创建forum 的方法由继承forumfactory 的子类去完成.
public abstract Forum createForum(String name, String description)
                   throws UnauthorizedException, ForumAlreadyExistsException;
….
}

单件(Singleton) 模式
Singleton 模式通常有几种形式:
public class Singleton {
private static Singleton _instance = new Singleton();
private Singleton() {  …  }
public static Singleton getInstance() {   return _instance;  }
}
调用方法:Singleton.getInstance();
第二种形式:
public class Singleton {
private static Singleton _instance = null;
private Singleton() {  …  }
public static Singleton getInstance() {
if (_instance==null)   _instance=new Singleton()
return _instance;
}
}
调用方法:Singleton.getInstance();
单件模式一般在框架的系统级常常有使用:
使用一个单件注册表(registry of singleton)。可能的Singleton类的集合不是由Instance定义的,Singleton类可以根据名字在一个众所周知的注册表中注册它们的单件实例。
这个注册表在字符串名字和单件之间建立映射。当Instance需要一个单件时,它参考注册表,根据名字请求单件。注册表查询相应的单件(如果存在的话)并返回它。
这个方法使得Instance不再需要知道所有可能的Singleton类或实例。它所需要的只是所有Singleton类的一个公共的接口,该接口包括了对注册表的操作:

代理(Proxy)模式 
是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构。
Proxy 应用范围很广,现在流行的分布计算方式RMI 和Corba 等都是Proxy 模式的应用.
为什么要使用Proxy 
1. 授权机制 不同级别的用户对同一对象拥有不同的访问权利。
2. 某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动。
两种方式:

图略

在Java1.3之后甚至引入了动态代理机制:
import java.lang.reflect.*; //反射包必须包括以支持RTTI
public class DynamicProxyDemo { 
public static void main(String[] clargs) { 
Foo prox = (Foo)Proxy.newProxyInstance( Foo.class.getClassLoader(), 
new Class[]{ Foo.class }, new InvocationHandler() { 
public Object invoke( Object proxy, Method method, Object[] args) {  
System.out.println( “InvocationHandler called:” + “\n\tMethod = ” + method); 
if (args != null) {  System.out.println(“\targs = “); 
for (int i = 0; i < args.length; i++) 
System.out.println(“\t\t” + args[i]); 

return null; 
} }); 
prox.f(“hello”);   prox.g(47);   prox.h(47, “hello”); 
} }
 Public class Foo{   Void f(String h){  System.out.println(“”+h);  } Void g(int m){System.out.println(“”+m);}
Void h(int k, Stringo){ System.out.println(“”+k+o);}}
以上示例中DynamicProxyDemo便作了类Foo的代理,且动态的实现了代理其功能。

Adapter(适配器)模式

图略

1. 可实现提供的接口,但是如果我们没有源代码,或者,我们不愿意为了一个应用而修改各自的接口,可使用适配器模式。
最典型的莫过于Java.awt中Event包,例如当我们要实现一个Window退出功能,就必须要实现WindowListener接口中的所有方法,否则不可实例化,无效。于是包中便又提供了另外适配器类WindowAdapter,而只要实现其中的windowClosing方法便可以了。实际上在适配器类WindowAdapter中也是实现WindowListener接口,只不过其余的方法全部被实现成空方法,仅留下windowClosing方法供子类来实现。
2. 适配器(Adaper)接受一种类型,并为其它类型产生一个接口。 当你手头有某个类,而你需要的却是另外一个类,你可以通过Adapter来解决问题。唯一需要做的就是产生出你需要的那个类,有多种方法可以完成这种配接。
示例如下:
class WhatIHave{  public void g() {}  public void h() {}  }
interface WhatIWant{  void f();  }
class SurrogateAdapter implements WhatIWant{
   WhatIHave whatIHave;
   public SurrogateAdapter(WhatIHave wih){  whatIHave = wih; }
   public void f(){   whatIHave.g();   whatIHave.h();  }
}
class WhatIUse{// Approach 1:通过传递的接口进行:
    public void op(WhatIWant wiw){  wiw.f();  }
}
class WhatIUse2 extends WhatIUse{// Approach 2: build adapter use into op():
   public void op(WhatIHave wih){ new SurrogateAdapter(wih).f(); }
}
class WhatIHave2 extends WhatIHave implements WhatIWant{// Approach 3   
public void f(){  g();   h(); }
}
class WhatIHave3 extends WhatIHave{ // Approach 4: use an inner class:
 private class InnerAdapter implements WhatIWant 
{  public void f(){ g();  h();  }  }
public WhatIWant whatIWant(){  return new InnerAdapter(); }
}
public class use_adapter_pattern{
    public static void main(String args[]){
     WhatIUse whatIUse = new WhatIUse();
     WhatIHave whatIHave = new WhatIHave();
     WhatIWant adapt= new SurrogateAdapter(whatIHave);

 WhatIUse2 whatIUse2 = new WhatIUse2();
     WhatIHave2 whatIHave2 = new WhatIHave2();
     WhatIHave3 whatIHave3 = new WhatIHave3();

whatIUse.op(adapt);
     whatIUse2.op(whatIHave);
     whatIUse.op(whatIHave2);
     whatIUse.op(whatIHave3.whatIWant());
   }}

图略

原型(protype)模式

图略

举例如下:

框架系统级往往会使用一个原型管理器当一个系统中原型数目不固定时(也就是说,它们可以动态创建和销毁),要保持一个可用原型的注册表。客户不会自己来管理原型,但会在注册表中存储和检索原型。客户在克隆一个原型前会向注册表请求该原型。我们称这个注册表为原型管理器(prototype manager)。原型管理器是一个关联存储器( associative store),它返回一个与给定关键字相匹配的原型。它有一些操作可以用来通过关键字注册原型和解除注册。客户可以在运行时更改甚或浏览这个注册表。这使得客户无需编写代码就可以扩展并得到系统清单。
例如Structs中在RequestProcess.java中processActionCreate(request, response, mapping)方法中便有:
/* Acquire the Action instance we will be using (if there is one)*/
 String className = mapping.getType()
 Action instance = null
 synchronized (actions) {
    /*Return any existing Action instance of this class*/
    /*先在 actions中找 existing  Action instance,并返回*/
    instance = (Action) actions.get(className)
   /*若 actions中不存在  Create and return a new Action instance*/
     instance = (Action) RequestUtils.applicationInstance(className)
            ->applicationClass(className).newInstance() //典型的动态的工厂方法的应用
 instance.setServlet(this.servlet)
     actions.put(className, instance)   //典型的原型管理器的使用
  }
状态变化的时候使用数据成员,行为变化的时候使用多态。

装饰器Decorator(油漆工)模式

图略

为何说是油漆工?
动态给一个对象添加一些额外的职责,就象在墙上刷油漆.,可以任意增加颜料(功能)。
使用Decorator 模式相比用生成子类方式达到功能的扩充显得更为灵活。
而使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的。而Decorator 提供了”即插即用”的方法,在运行期间决定何时增加何种功能。
当使用派生类方法产生过多(不灵活的)类的时候考虑应用Decorator模式。
所有外覆在原始对象上的装饰类都必须有同样的基本接口。Decorator通过包裹(wrapping)一个组件来给它增加功能, 但Decorator兼容它所包裹的那个类的接口, 这样,对component的包裹就是透明的。 Decorator自身又可以被(别的Decorator)包裹而不丧失其透明性。

实际上Java 的I/O API 就是使用Decorator 实现的,I/O 变种很多,如果都采取继承方法,将会产生很多子类,显然相当繁琐:
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
另外Java中经常使用的内部匿名类等也是Decorator的具体实现:
Frame f = new Frame(“FTP Client”); 
f.addWindowListener(new WindowAdapter()
{ public void windowClosing(WindowEvent e){ System.exit(0); } 
});
另外典型示例如下:
interface DrinkComponent{   String getDescription();  float getTotalCost();  }
abstract class Decorator implements DrinkComponent{
   protected DrinkComponent component;
   Decorator(DrinkComponent component){  this.component = component;  }
   public float getTotalCost(){  return component.getTotalCost(); }
public abstract String getDescription();
}
class Mug implements DrinkComponent{
   public String getDescription(){  return “mug”; }
   public float getTotalCost(){  return 0;  }
}
class Decaf extends Decorator{
   private String description = ” decaf”;
   public Decaf(DrinkComponent component){  super(component);  }
   public String getDescription(){  return component.getDescription() +description; }
}
class SteamedMilk extends Decorator{
   private float cost = 0.25f;   private String description = ” steamed milk”;
   public SteamedMilk(DrinkComponent component){  super(component); }
   public float getTotalCost(){  return component.getTotalCost() + cost; }
   public String getDescription() {  return component.getDescription() +description; }
}
class Whipped extends Decorator{
   private float cost = 0.25f;   private String description = ” whipped cream”;
   public Whipped(DrinkComponent component) {  super(component);  }
   public float getTotalCost() {  return component.getTotalCost() + cost; }
   public String getDescription(){  return component.getDescription() +description; }
}
class Chocolate extends Decorator{
    private float cost = 0.25f;   private String description = ” chocolate”;
    public Chocolate(DrinkComponentcomponent){   super(component);  }
    public float getTotalCost(){  return component.getTotalCost() + cost; }
    public String getDescription(){  return component.getDescription() +description; }
}
public class decorator_pattern{
   public static void main(String[] args){    
        // Create a decaf cafe mocha with whipped cream
        DrinkComponent cafeMocha = new Espresso(
                            new SteamedMilk(new Chocolate(new Whipped(
                                    new Decaf(new Mug())))));
        System.out.println(cafeMocha.getDescription().trim() + “: $”
                                 + cafeMocha.getTotalCost());
   }
}
注:这种方法当然提供了最灵活的体积 。你只需从很少几个组件(components)里挑出你需要的,但是拼接这些“关于咖啡的描述”就变的十分费劲。折衷是通过创建合适大小的供常规选择的菜单来实现的,这个菜单基本上是不怎么变化的,但是如果你想给这些基本饮品加点伴侣(比如whipped cream, decaf等),那你可以用decorators来改变基本饮品。这其实也是大多数咖啡馆提供的那种菜单。

Observer(观察者)模式

图略

是比较常用的一个模式,尤其在界面设计中应用广泛。
Observer 能自动观察到这种变化,并能进行及时的update 或notify 动作。
Java 的API 还为为我们提供现成的Observer 接口Java.util.Observer.我们只要直接使用它就可以。我们必须继承 Java.util.Observer 才能真正使用它。
什么时候用Observer?
当你需要用完全动态的方式分离呼叫源和被呼叫代码的时候 Observer模式都是你的首选。Observer模式允许你通过挂钩程序(hook point)改变代码。从本质上说,Observer模式是完全动态的。它经常被用于需要根据其它对象的状态变化来改变自身(状态)的场合,
而且它还经常是事件管理系统(event management)的基本组成部分。
典型应用:
Java中的事件处理是典型的Observer模式。包括被监视者添加监视器(addXXListener)而监视者需要实现(implements XXListener)事件发生时的处理函数。
示例如下:
public class product extends Observable{
private String name;  private float price;
public String getName(){ return name;}
public void setName(){
    this.name=name;
    setChanged();  //设置变化点
    notifyObservers(name);
}
public float getPrice(){ return price;}
public void setPrice(){
      this.price=price;
      setChanged();  //设置变化点
      notifyObservers(new Float(price));
}
.  public void saveToDb(){…}  //以下可以是数据库更新 插入命令
}
//观察者NameObserver 主要用来对产品名称(name)进行观察的
public class NameObserver implements Observer{
private String name=null;
public void update(Observable obj,Object arg){
        if (arg instanceof String){
             name=(String)arg;
            System.out.println(“NameObserver :name changet to “+name);
       }
}}
//观察者PriceObserver 主要用来对产品价格(price)进行观察的
public class PriceObserver implements Observer{
private float price=0;
public void update(Observable obj,Object arg){
     if (arg instanceof Float){
        price=((Float)arg).floatValue();
        System.out.println(“PriceObserver :price changet to “+price);
     }
}}
public class ObserverTest {
public static void main(String args[]){
    Product product=new Product();
    NameObserver nameobs=new NameObserver();
    PriceObserver priceobs=new PriceObserver();
//加入观察者
    product.addObserver(nameobs);
    product.addObserver(priceobs);
    product.setName(“橘子红了”);
    product.setPrice(9.22f);
}}
你会发现下面信息:   NameObserver :name changet to 橘子红了
PriceObserver :price changet to 9.22
这说明观察者在行动了!!
其实,Java中观察者模式原理在于:由Observable内定义了Observer接口的对象列表,当由Observable继承的对象(被观察者)发生改变时,会触发Observable的notify方法去遍历调用所有与被观察者有关系的Observer接口的实现对象具体实现的接口中的update方法,从而实现了“据被观察者变化实时改变观察者行为”的目的。

Flyweight 模式

图略

Flyweight 模式作用:
为避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类)。
是一个提高程序效率和性能的模式,会大大加快程序的运行速度。
应用场合很多:比如你要从一个数据库中读取一系列字符串,这些字符串中有许多是重复的,那么我们可以将这些字符串储存在Flyweight 池(pool)中。
实例如下:
public interface Flyweight{  public void operation( ExtrinsicState state );  }
public class ConcreteFlyweight implements Flyweight {
   private IntrinsicState state;
   public void operation( ExtrinsicState state ) {
       //具体操作
   }
}
public class FlyweightFactory {
   private Hashtable flyweights = new Hashtable();  //Flyweight pool
      public Flyweight getFlyweight( Object key ) {
           Flyweight flyweight = (Flyweight) flyweights.get(key);
           if( flyweight == null ) {
              flyweight = new ConcreteFlyweight();
              flyweights.put( key, flyweight );
           }
        return flyweight;
      }
}
从调用上看,好象是个纯粹的Factory 使用,但其奥妙就在于Factory 的内部设计上.
调用:  FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1 = factory.getFlyweight( “Fred” );
Flyweight fly2 = factory.getFlyweight( “Wilma” );

命令(Command)模式

图略

为何使用Command模式?
从本质上说,Command就是一个函数对象:一个被封装成对象的方法。
通过把方法封装到一个对象,你可以把它当作参数传给其它方法或者对象,
让它们在实现你的某个请求(request)的时候完成一些特殊的操作。
你可能会说Command其实就是一个把数据成员换成行为的messenger(因为它的目的和使用方法都很直接)。Commands其实就是回调函数(callbacks)的面向对象替代品。
Command模式最重要的一点就是:你可以通过它把想要完成的动作(action)交给一个方法或者对象。
使用Command 模式的一个好理由还因为它能实现Undo 功能。
每个具体命令都可以记住它刚刚执行的动作,并且在需要时恢复。
Command 模式在界面设计中应用广泛。
Command模式的应用:
其实在C++STL中的函数对象(仿函数,functor)便是一种Command模式。 
#include<iostream.h>
#include<vector>
#include<algorithm>
//Simple function object that prints the passed argument
Class PrintInt{
Public : Void operator() (int elem)const{   Cout<<elem<<’ ’;   }
};
Int main(){
Vector<int> coll;
for(int i=1; i<=9;++i){   coll.push_back(i);   }
for_each( coll.begin(),  coll.end(),  PrintInt());
/*其实for_each()算法定义如下:
  Namespace std{
      Template<class Iterator,  class operation>
       Operation for_each(Iterator act,  Iterator end,  operation op) {
           While(act!=end){
               Op(*act);
               ++act;
           }
         Return op;
      }
}
For_each()使用暂时对象op(仿对象),针对每个元素调用op(*act)。若第三参数是个一般函数,以*act为参数调用之。若第三参数是个仿函数则以*act为参数,调用仿函数op的operation()。For_each()调用 PrintInt::operation()(*act)。
*/
cout<<endl;
}
而Java 的Swing 中菜单命令都是使用Command 模式。
Java中的一些个标志接口如Serializable是Command模式的一种特化。
示例如下:
public interface Command {  public abstract void execute ( );  }
public class Engineer implements Command {
  public void execute( ) {  //do Engineer’s command  }
}
public class Programmer implements Command {
public void execute( ) {  //do programmer’s command  }
}
public class Politician implements Command{
   public void execute( ) {  //do Politician’s command    }
}
public class producer{
  public static List produceRequests() {
    List queue = new ArrayList();
    queue.add( new DomesticEngineer() );
    queue.add( new Politician() );
    queue.add( new Programmer() );
    return queue;
  }}
public class TestCommand {
public static void main(String[] args) {
     List queue = Producer.produceRequests();
     for (Iterator it = queue.iterator(); it.hasNext(); )
                 //取出List 中东东,其他特征都不能确定,只能保证一个特征是100%正确,
                 // 他们至少是接口Command 的”儿子”.所以强制转换类型为接口Command((Command)it.next()).execute();
}}
策略(Strategy)模式
定义一系列的算法,把这些算法一个个封装成单独的类。从而达到了在运行期间,可以自由切换算法的目的。

图略

Strategy模式的应用:
Strategy 适合被用于下列场合:
1.以不同的格式保存文件。
2.以不同的算法压缩文件。
3.以不同的算法截获图象。
4.以不同的格式输出同样数据的图形,比如曲线 或框图bar 等。
另外策略模式不仅在面向对象中大量使用,在面向过程的设计中也有使用:如C的函数指针,空指针均为这种设计模式提供了天然的土壤。C库函数中的qsort(快速排序)便是最典型的例证了:
void qsort(void * _base, size_t _nmemb, size_t _size, int(*_compar)(const void *, const void *));
其中函数指针_compar便是用来替换各种不同比较策略的。
Strategy与Command的使用区别:
Command模式在编码阶段提供灵活性,而Strategy模式的灵活性在运行时体现出来。
Java里Strategy模式的例子:comparator objects(Comparator 对象方法)。
示例如下:
public abstract class RepTempRule{
  protected String oldString=””;
  public void setOldString(String oldString){ this.oldString=oldString; }
  protected String newString=””;
  public String getNewString(){  return newString;  }
  public abstract void replace() throws Exception;
}
public class RepTempRuleOne extends RepTempRule{
   public void replace() throws Exception{
     newString=oldString.replaceFirst(“aaa”, “bbbb”)  //replaceFirst 是jdk1.4 新特性
     System.out.println(“this is replace one”);
   }
}
public class RepTempRuleTwo extends RepTempRule{
   public void replace() throws Exception {
     newString=oldString.replaceFirst(“aaa”, “ccc”)
     System.out.println(“this is replace Two”);
   }
}
public class RepTempRuleSolve {
  private RepTempRule strategy;
  public RepTempRuleSolve(RepTempRule rule){  this.strategy=rule;  }
  public String getNewContext(Site site,String oldString) {
     return strategy.replace(site,oldString);
  }
  public void changeAlgorithm(RepTempRule newAlgorithm) {  strategy = newAlgorithm; }
}
调用如下:
public class test{
public void testReplace(){
  //使用第一套替代方案
  RepTempRuleSolve solver=new RepTempRuleSolve(new RepTempRuleOne());
  solver.getNewContext(site,context);
  //使用第二套
  solver=new RepTempRuleSolve(new RepTempRuleTwo());
  solver.getNewContext(site,context);
}}

TemplateMethod模式

图略

Template Method模式的特征:
它是在基类里定义的, 而且不能够被(派生类)更改。有时候它是私有方法(private method),但实际上它经常被声明为final。它通过调用其它的基类方法(覆写过的)来工作,但它经常是作为初始化过程的一部分被调用的,这样就没必要让客户端程序员能够直接调用它了。基类的构造函数负责完成必要的初始化和启动应用程序“引擎”(template method)(在一个图形用户界面(GUI)程序里,这个“引擎”通常是“主事件循环(main event loop)”)。
客户端程序员只需简单的提供customize1( )和 customize2( )方法的定义,整个程序就可以跑起来了。
需注意Template Method模式与C++或Java中的Template不完全一样,Template Method模式中包含C++或Java中的Template的方式。
Template Method模式的应用:

图略

现今的绝大部分软件开发框架均是Template Method模式的最明显应用。
比如说MFC:
CObject
|_CCmdTarget
  |__CWinThread
  |   |__CWinApp
  |      |__CMyWinApp  //必须由用户实现
  |__CWnd
  |   |__CView
  |   |   |__CMyView    //必须由用户实现
  |   |__CFrameWnd
  |      |__CMyFrameWnd  //由用户实现(可选)
  |__CDocument
     |__CMyDoc   //必须由用户实现

再如Structs:

图略

另外,我们给出一个最简单的示例如下:
abstract class ApplicationFramework{
   public ApplicationFramework(){  templateMethod();  }
   abstract void customize1(); //系统提供给用户实现的客户化函数部分
   abstract void customize2(); //系统提供给用户实现的客户化函数部分
   final void templateMethod(){
       //在一个图形用户界面(GUI)程序里,这个“引擎”
       // 通常是“主事件循环(main event loop)”
       for(int i = 0; i < 5; i++) {
         customize1();  customize2();
       }
   }
}
// Create a new “application”:
// 用户具体实现客户化函数部分
class MyApp extends ApplicationFramework {
    void customize1(){  System.out.print(“Hello “);  }
    void customize2(){  System.out.println(“World!”);  }
}
public class templateMethod_pattern{
   public static void main(String args[]){
       MyApp app = new MyApp();
   }
}
MVC模式:
MVC最早是由Alan Kay在设计的Smalltalk中使用,而随着现今网络的席卷,BS模式的架构被大量的使用,故MVC这种优良的模式也被广泛的使用。J2EE中提供了Servlet, EJB, Jsp便很好的支持了MVC。
MVC交互

图略

另外Structs中交互流程:

图略

外观模式(Facade):
在应用中,经常需要对数据库操作,每次都写上述一段代码肯定比较麻烦,需要将其中不变的
部分提炼出来,做成一个接口,这就引入了facade 外观对象.如果以后我们更换
Class.forName 中的<driver>也非常方便,比如从Mysql 数据库换到Oracle 数据库,只要更
换facade 接口中的driver 就可以.
我们做成了一个Facade 接口,使用该接口,上例中的程序就可以更改如下:
public class DBCompare {
String sql = “SELECT * FROM <table> WHERE <column name> =  “;
try {
Mysql msql=new mysql(sql);
prep.setString( 1, “<column value>” );
rset = prep.executeQuery();
if( rset.next() ) {
System.out.println( rset.getString( “<column name” ) );
}
} catch( SException e ) {
e.printStackTrace();
} finally {
mysql.close();
mysql=null;
}
}
facade 实际上是个理顺系统间关系,降低系统间耦合度的一个常用的办法,也许你已经不知不觉在使用,尽管不知道它就是fa ade。

组合模式(Composite):
将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。
示例:
public abstract class Equipment
{
private String name;
public abstract double netPrice();//网络价格
public abstract double discountPrice();//折扣价格
public boolean add(Equipment equipment) { return false; }//增加部件方法
public boolean remove(Equipment equipment) { return false; }//删除部件方法
//注意这里,这里就提供一种用于访问组合体类的部件方法。
public Iterator iter() { return null; }
public Equipment(final String name) { this.name=name; }
}
public class Disk extends Equipment
{
public Disk(String name) { super(name); }
public double netPrice() { return 1.; }//定义Disk 网络价格为1
public double discountPrice() { return .5; }//定义了disk 折扣价格是0.5 对折。
}

abstract class CompositeEquipment extends Equipment{
  private int i=0;
  private Lsit equipment=new ArrayList();//定义一个Vector 用来存放’儿子’
  public CompositeEquipment(String name) { super(name); }
  public boolean add(Equipment equipment) {
    this.equipment.add(equipment);
    return true;
  }
public double netPrice(){
double netPrice=0.;
Iterator iter=equipment.iterator();
for(iter.hasNext())   netPrice+=((Equipment)iter.next()).netPrice();
return netPrice;
}
public double discountPrice(){
double discountPrice=0.;
Iterator iter=equipment.iterator();
for(iter.hasNext())   discountPrice+=((Equipment)iter.next()).discountPrice();
return discountPrice;
}
//注意这里,这里就提供用于访问自己组合体内的部件方法。
//上面dIsk 之所以没有,是因为Disk 是个单独(Primitive)的元素.
public Iterator iter(){  return equipment.iterator() ;  }//重载Iterator 方法
public boolean hasNext() { return i<equipment.size(); }//重载Iterator 方法
public Object next(){
if(hasNext())  return equipment.elementAt(i++);
else   throw new NoSuchElementException();
}
}
public class Chassis extends CompositeEquipment{
  public Chassis(String name) { super(name); }
  public double netPrice() { return 1.+super.netPrice(); }
  public double discountPrice()  { return 0.5+super.discountPrice(); }
}
public class Cabinet extends CompositeEquipment{
  public Cabinet(String name) { super(name); }
  public double netPrice() { return 1.+super.netPrice(); }
  public double discountPrice() { return 0.3+super.discountPrice(); }
}
客户端调用Composite 代码:
Cabinet cabinet=new Cabinet(“Tower”);
Chassis chassis=new Chassis(“PC Chassis”);
cabinet.add(chassis);  //将PC Chassis 装到Tower 中 (将盘盒装到箱子里)
chassis.add(new Disk(“10 GB”));  //将一个10GB 的硬盘装到 PC Chassis (将硬盘装到盘盒里)
System.out.println(“netPrice=”+cabinet.netPrice());  //调用 netPrice()方法;
System.out.println(“discountPrice=”+cabinet.discountPrice());
实际上Composite 使用Iterator 遍历了整个树形结构,寻找同样包含这个方法的对象并实现调用执行。
Composite 是个很巧妙体现智慧的模式,在实际应用中,如果碰到树形结构,我们就可以尝试是否可以使用这个模式。

迭代器(Iterator)模式:
迭代器将算法从它所使用的特定类型的容器中分离出来。
这就意味着在描述算法的时候,可以不必考虑它所操作的特定序列。
更为一般情况,用迭代器写的任何代码都与它所操作的数据结构相分离,
这样一来这些代码就更为通用并且易于重用。
在C++ STL中便存在大量的迭代器与容器,算法一起联合使用。
在STL中迭代器是一种“能够遍历某个序列内所有元素”的对象。它可通过与一般指针一致的接口来完成自己的工作。迭代器奉行一个纯抽象概念:任何东西,只要行为类似迭代器就是一种迭代器。不同的迭代器具有不同的能力(指行进或存取能力)。而不同算法需要不同的迭代器。故迭代器被分成不同的类型(如:InputIterator, OutputIterator, ForwardIterator, BidirectionalIterator, RandomAccessIterator等)。
先举个最简单使用STL迭代器来操作的例子:
#include<vector>
# include<iostream>
using namespace std; //在这儿是必须的
int main()
{
   vector<int> coll;
   for(int i=0; i<3;i++)  coll.push_back(i);
   vector<int>::iterator pos; //由此可看出迭代器被包含在容器中
   for(pos=coll.begin(); pos<coll.end(); ++pos)  //使用STL容器Vector提供的迭代器
cout<<*pos<<‘ ’;
   cout<<endl;
}
我们再来实现一个自己的迭代器:
# include <iterator>
template <class Container>
class my_insert_iterator:public std::iterator<std::output_iterator_tag, void, void, void, void>
{
Protected: Container&container;
Public: explicit my_insert_iterator(Container& c):container(c){}
my_insert_iterator<Container>&operator=(const typename Container::value_type& value){
   container.insert(value);
return *this;
}
my_insert_iterator<Container>&operator*(){
   return *this;
}
my_insert_iterator<Container>&operator++(){
   return *this;
}
my_insert_iterator<Container>&operator++(int){
   return *this;
}
};
template <class Container>
inline my_insert_iterator<Container> my_insert(Container& c){
    return  my_insert_iterator<Container>(c);
}
使用自己编写的迭代器(my_insert_iterator):
# include<iostream>
# include<set>
# include<algorithm>
using namespace std; //在这儿是必须的
//包含迭代器的头文件
int main()
{
   set<int> coll;
   my_insert_iterator<set<int>> iter(coll);
   *iter=1;
   iter++;
   *iter=2;
   iter++;
   *iter=3;
//打印输出迭代器内容
   my_insert(coll)=44;
   my_insert(coll)=55;
//打印输出 插入的元素
   int vals[]={22,33,44};
   Copy(vals, vals+(sizeof(vals)/sizeof(vals[0])), my_insert(coll)); //使用算法插入元素
    //打印出内容
}
再来看看在java中的迭代器使用
自己来编写一个 迭代器的外覆类:
我们不可能重写现有的Java类库,它已经包含了枚举器和迭代器。但是,我们可以用Decorator模式简单的创建一个枚举器或者迭代器的外覆类,产生一个具有我们想要的迭代行为(本例中,指在类型不正确的时候抛出RuntimeException异常)的新对象,而这个新对象跟原来的枚举器或者迭代器有相同的接口,这样一来,它就可以用在相同的场合(或许你会争论说这实际上是Proxy模式,但是从它的目的(intent)来说它更像Decorator模式)。
保证此迭代器内部所有元素均相同,这个新对象覆盖了原来迭代器的接口 以保证操作的兼容。
import java.util.*;
public class TypedIterator implements Iterator{
     private Iterator imp;
     private Class type;
     public TypedIterator(Iterator it, Class type){
        imp = it;
        this.type = type;
     }
     public boolean hasNext(){  return imp.hasNext();  }
     public void remove() { imp.remove(); }
     public Object next(){
         Object obj = imp.next();
         if(!type.isInstance(obj))
             throw new ClassCastException(
                            “TypedIterator for type ” + type +
                            ” encountered type: ” + obj.getClass());
         return obj;
     }
}

其他还有些模式,由于我还没来得及学习,故这便不再叙述。
另外,其实各个模式间的联系也非常紧密,比如说:Factory Method几乎其他模式都会用到。单件与原型也经常一起使用。另外有种说法:MVC模式是Observer设计模式的扩展形式也有些道理,等等。如今,找寻其他种模式也是很好的方向。

Design Pattern的应用及延伸:
1 Analysis Pattern(分析模式)
2 Refactor (主要是代码重构)
3 Framework (尤其是freeware框架) 
4 与case工具的集成
如Rational Rose:

图略

注:由于粘图比较麻烦,省去了一些图,表。

MySQL查询优化系列讲座之数据类型与效率(转)

  这一部分提供了如何选择数据类型来帮助提高查询运行速度的一些指导:

  在可以使用短数据列的时候就不要用长的。如果你有一个固定长度的CHAR数据列,那么就不要让它的长度超出实际需要。如果你在数据列中存储的最长的值有40个字符,就不要定义成CHAR(255),而应该定义成CHAR(40)。如果你能够用MEDIUMINT代替BIGINT,那么你的数据表就小一些(磁盘I/O少一些),在计算过程中,值的处理速度也快一些。如果数据列被索引了,那么使用较短的值带来的性能提高更加显著。不仅索引可以提高查询速度,而且短的索引值也比长的索引值处理起来要快一些。

  如果你可以选择数据行的存储格式,那么应该使用最适合存储引擎的那种。对于MyISAM数据表,最好使用固定长度的数据列代替可变长度的数据列。例如,让所有的字符列用CHAR类型代替VARCHAR类型。权衡得失,我们会发现数据表使用了更多的磁盘空间,但是如果你能够提供额外的空间,那么固定长度的数据行被处理的速度比可变长度的数据行要快一些。对于那些被频繁修改的表来说,这一点尤其突出,因为在那些情况下,性能更容易受到磁盘碎片的影响。

  · 在使用可变长度的数据行的时候,由于记录长度不同,在多次执行删除和更新操作之后,数据表的碎片要多一些。你必须使用OPTIMIZE TABLE来定期维护其性能。固定长度的数据行没有这个问题。

  · 如果出现数据表崩溃的情况,那么数据行长度固定的表更容易重新构造。使用固定长度数据行的时候,每个记录的开始位置都可以被检测到,因为这些位置都是固定记录长度的倍数,但是使用可变长度数据行的时候就不一定了。这不是与查询处理的性能相关的问题,但是它一定能够加快数据表的修复速度。

  尽管把MyISAM数据表转换成使用固定长度的数据列可以提高性能,但是你首先需要考虑下面一些问题:

  · 固定长度的数据列速度较快,但是占用的空间也较大。CHAR(n)列的每个值(即使是空值)通常占n个字符,这是因为把它存储到数据表中的时候,会在值的后面添加空格。VARCHAR(n)列占有的空间较小,因为只需要分配必要的字符个数用于存储值,加上一两个字节来存储值的长度。因此,在CHAR和VARCHAR列之间进行选择的时候,实际上是时间与空间的对比。如果速度是主要的考虑因素,那么就使用CHAR数据列获取固定长度列的性能优势。如果空间很重要,那么就使用VARCHAR数据列。总而言之,你可以认为固定长度的数据行可以提高性能,虽然它占用了更大的空间。但是对于某些特殊的应用程序,你可能希望使用两种方式来实现某个数据表,然后运行测试来决定哪种情况符合应用程序的需求。

  · 即使愿意使用固定长度类型,有时候你也没有办法使用。例如,长于255个字符的字符串就无法使用固定长度类型。

  MEMORY数据表目前都使用固定长度的数据行存储,因此无论使用CHAR或VARCHAR列都没有关系。两者都是作为CHAR类型处理的。

  对于InnoDB数据表,内部的行存储格式没有区分固定长度和可变长度列(所有数据行都使用指向数据列值的头指针),因此在本质上,使用固定长度的CHAR列不一定比使用可变长度VARCHAR列简单。因而,主要的性能因素是数据行使用的存储总量。由于CHAR平均占用的空间多于VARCHAR,因此使用VARCHAR来最小化需要处理的数据行的存储总量和磁盘I/O是比较好的。

  对于BDB数据表,无论使用固定长度或可变长度的数据列,差别都不大。两种方法你都可用试一下,运行一些实验测试来检测是否存在明显的差别。

  把数据列定义成不能为空(NOT NULL)。这会使处理速度更快,需要的存储更少。它有时候还简化了查询,因为在某些情况下你不需要检查值的NULL属性。

  考虑使用ENUM数据列。如果你拥有的某个数据列的基数很低(包含的不同的值数量有限),那么可以考虑把它转换为ENUM列。ENUM值可以被更快地处理,因为它们在内部表现为数值。

  使用PROCEDURE ANALYSE()。运行PROCEDURE ANALYSE()可以看到数据表中列的情况:

SELECT * FROM tbl_name PROCEDURE ANALYSE();
SELECT * FROM tbl_name PROCEDURE ANALYSE(16,256);

  输出的每一列信息都会对数据表中的列的数据类型提出优化建议。第二个例子告诉PROCEDURE ANALYSE()不要为那些包含的值多于16个或者256字节的ENUM类型提出建议。如果没有这样的限制,输出信息可能很长;ENUM定义通常很难阅读。

  根据的PROCEDURE ANALYSE()输出信息,你可能发现,可以修改自己的数据表来利用那些效率更高的数据类型。如果你决定改变某个数据列的类型,需要使用ALTER TABLE语句。

  使用OPTIMIZE TABLE来优化那些受到碎片影响的数据表。被大量修改的数据表,特别是那些包含可变长度数据列的表,容易遭受碎片的影响。碎片很糟糕,因为它会导致用于存储数据表的磁盘块形成无用空间(空洞)。随着时间的推移,为了得到有效的数据行,你必须读取更多的块,性能就会降低。这会出现在任何可变长度的数据行上,但是对于BLOB或TEXT数据列尤其突出,因为它们的长度差异太大了。在正常情况下使用OPTIMIZE TABLE会防止数据表的性能降低。OPTIMIZE TABLE可以用于MyISAM和BDB数据表,但是defragments只能用于MyISAM数据表。任何存储引擎中的碎片整理方法都是用mysqldump来转储(dump)数据表,接着使用转储的文件删除并重新建立那些数据表:

% mysqldump –opt db_name tbl_name > dump.sql
% mysql db_name < dump.sql

  把数据打包放入BLOB或TEXT数据列。使用BLOB或TEXT数据列存储打包(pack)的数据,并在应用程序中进行解包(unpack),使你能够在一次检索操作中得到需要的任何信息,而不需要进行多次检索。它对那些很难用标准的数据表结构表现的数据值和频繁变化的数据值也是有帮助的。

  解决这个问题的另一种方法是让那些处理Web窗体的应用程序把数据打包成某种数据结构,然后把它插入到单个BLOB或TEXT数据列中。例如,你可以使用XML表示调查表回复,把那些XML字符串存储在TEXT数据列中。由于要对数据进行编码(从数据表中检索数据的时候还需要解码),它会增加客户端的开销,但是可以简化数据结构,而且它还消除了那些因为改变了调查表的内容而必须改变数据表结构的需求。

  另一方面,BLOB和TEXT值也会引起自己的一些问题,特别是执行了大量的删除或更新操作的时候。删除这种值会在数据表中留下很大的”空洞”,以后填入这些”空洞”的记录可能长度不同(前面讨论的OPTIMIZE TABLE提出解决这个问题的一些建议)。

  使用合成的(synthetic)索引。合成的索引列在某些时候是有用的。一种办法是根据其它的列的内容建立一个散列值,并把这个值存储在单独的数据列中。接下来你就可以通过检索散列值找到数据行了。但是,我们要注意这种技术只能用于精确匹配的查询(散列值对于类似<或>=等范围搜索操作符是没有用处的)。我们可以使用MD5()函数生成散列值,也可以使用SHA1()或CRC32(),或者使用自己的应用程序逻辑来计算散列值。请记住数值型散列值可以很高效率地存储。同样,如果散列算法生成的字符串带有尾部空格,就不要把它们存储在CHAR或VARCHAR列中,它们会受到尾部空格去除的影响。

  合成的散列索引对于那些BLOB或TEXT数据列特别有用。用散列标识符值查找的速度比搜索BLOB列本身的速度快很多。

  在不必要的时候避免检索大型的BLOB或TEXT值。例如,SELECT *查询就不是很好的想法,除非你能够确定作为约束条件的WHERE子句只会找到所需要的数据行。否则,你可能毫无目的地在网络上传输大量的值。这也是BLOB或TEXT标识符信息存储在合成的索引列中对我们有所帮助的例子。你可以搜索索引列,决定那些需要的数据行,然后从合格的数据行中检索BLOB或TEXT值。

  把BLOB或TEXT列分离到单独的表中。在某些环境中,如果把这些数据列移动到第二张数据表中,可以让你把原数据表中的数据列转换为固定长度的数据行格式,那么它就是有意义的。这会减少主表中的碎片,使你得到固定长度数据行的性能优势。它还使你在主数据表上运行SELECT *查询的时候不会通过网络传输大量的BLOB或TEXT值。

  高效率地载入数据

  在大多数情况下,你所关注的是SELECT查询的优化,因为SELECT查询是最常见的查询类型,而且如何优化它们又不是太简单。与此形成对比,把数据载入数据库的操作就相对直接了。然而,你仍然可以利用某些策略来改善数据载入操作的效率。基本的原理如下所示:

  · 批量载入比单行载入的效率高,因为在每条记录被载入后,键缓存(key cache)不用刷新(flush);可以在这批记录的末尾刷新键缓存。键缓存刷新的频率减少得越多,数据载入的速度就越快。

  · 没有索引的数据表的载入速度比有索引的要快一些。如果存在索引,不但要把记录添加到数据文件中,还必须修改索引来反映新增的记录。

  · 较短的SQL语句比较长的SQL语句快,因为它们所涉及到服务器端分析过程较少,同时通过网络把它们从客户端发送到服务器上的速度也更快。

  其中有些因素看起来是次要的(尤其是最后一个),但是如果你载入的数据很多,那么即使很小的效率差异也会导致一定的性能差别。我们可以从前面的一般原理得出几条如何快速载入数据的实践结论:

  · LOAD DATA(所有形式的)比INSERT效率高,因为它是批量载入数据行的。服务器只需要分析和解释一条语句,而不是多条语句。同样,索引只需要在所有的数据行被处理过之后才刷新,而不是每行刷新一次。

  · 不带LOCAL的LOAD DATA比带有LOCAL的LOAD DATA的速度要快。不带LOCAL的时候,文件必须位于服务器上,而且你必须拥有FILE权限,但是服务器却可以直接从磁盘上读取文件。使用LOAD DATA LOCAL的时候,客户端读取文件并通过网络把它发送给服务器,速度慢一些。

  · 如果你必须使用INSERT,那么试着使用在一个语句中指定多个数据行的形式:

INSERT INTO tbl_name VALUES(…),(…),… ;

  在这个语句中指定的数据行越多,效果就越好。这会减少必要的语句数量,并最小化索引刷新的次数。这一条结论看起来与前面所讨论的”语句越短,执行速度越快”相矛盾,但是实际上并不矛盾。这儿所讨论的是同时插入多个数据行的一个INSERT语句所花费的开销比功能相同的多个单行INSERT语句的花费的开销要小一些,并且多行语句消耗的索引刷新开销也少一些。

  如果你使用mysqldump生成数据库备份文件,那么MySQL 4.1会默认地生成多行INSERT语句:它会激活–opt (优化)选项,而这个选项会激活–extended-insert选项,该选项生成多行INSERT语句,还存在其它一些选项也可以使数据被载入的时候,转储文件被处理的效率更高。对于MySQL 4.1以前的版本,你可以明确地指定–opt或–extended-insert选项。

  使用mysqldump的时候要避免使用–complete-insert选项;它生成的INSERT语句是每个数据行一条语句的,语句总共会很长,比多行语句需要的分析操作更多。

  · 如果你必须使用INSERT语句,那么在可能的情况下,对它们进行分组以减少索引的刷新。对于事务性的存储引擎,在单个事务中提交,而不是在自动提交(autocommit)模式下提交INSERT语句可以实现这样的功能:

START TRANSACTION;
INSERT INTO tbl_name … ;
INSERT INTO tbl_name … ;
INSERT INTO tbl_name … ;
COMMIT;

  对于非事务性的存储引擎,获取数据表上的写入锁,它被锁定的时候提交INSERT语句:

LOCK TABLES tbl_name WRITE;
INSERT INTO tbl_name … ;
INSERT INTO tbl_name … ;
INSERT INTO tbl_name … ;
UNLOCK TABLES;


  无论采用哪种方法,你得到的好处都是相同的:索引在所有的语句都被执行之后才刷新一次,而不是每个INSERT语句刷新一次索引。后面介绍了在自动提交模式下或数据表没有被锁定的时候发生的情况。

  · 对于MyISAM数据表,减少索引刷新的另外一个策略是使用DELAYED_KEY_WRITE表选项。使用这个选项的时候,数据行会像平常一样立即写入数据文件中,但是键缓存只是偶尔刷新一次,而不是在每次插入操作之后都需要刷新。如果要在服务器上全面地使用延迟索引刷新,那么就需要使用–delay-key-write选项来启动mysqld。在这种情况下,每个数据表的索引块写入操作都会被延迟,直到这些数据块必须为其它的索引值提供空间、或者执行了FLUSH TABLES命令、或者数据表被关闭的时候才执行操作。

  如果你选择了对MyISAM数据表使用延迟键写入,那么不正常的服务器关闭可能会引起索引值的丢失。这不是致命的问题,因为MyISAM索引可以依据数据行来进行修复,但是如果想让修复过程出现,你就必须使用–myisam-recover=FORCE选项来启动服务器。这个选项会使服务器在打开MyISAM数据表的时候检查它们,如果有必要就自动地修复它们。

  对于复制(replication)从属服务器,你可能希望使用–delay-key-write=ALL来延迟所有的MyISAM数据表索引的刷新,不管在主服务器上最初是如何建立它们的。

  · 使用压缩的客户端/服务器协议来减少网络上数据传输的数量。对于大多数MySQL客户端来说,我们都可以使用–compress命令行选项来指定它。通常,这个选项只是在较慢的网络上使用,这是因为压缩操作会花费大量的处理器时间。

  · 让MySQL替你插入默认值。也就是说,无论如何都不要给INSERT语句中那些可以赋予默认值的列指定值。平均起来,你的语句更短,减少了通过网络发送到服务器的字符数量。此外,由于语句包含的值较少,服务器执行的分析和值转换操作也较少。

  · 对于MyISAM数据表,如果你必须把大量的数据载入一个新表,最好建立不带索引的表,载入数据,然后建立索引,这样的工作次序的速度要快一些。一次性地建立索引比每行都更新索引的速度要快一些。对于已经带有索引的表,如果预先删除或禁止索引,后来再重新建立或者激活索引,那么数据载入的速度也要快一些。这些策略不能应用于InnoDB或BDB表,它们没有对分离的索引建立过程进行优化。

  如果你考虑使用删除或禁止索引的策略,把数据载入MyISAM数据表,那么在评估获得的优势的时候,就需要考虑整个环境。如果你把少量的数据载入大型的数据表中,那么在没有任何特殊准备工作的情况下,重新建立索引花费的时间可能比载入数据的时间还要长。

  要删除并且重新建立索引,需要使用DROP INDEX和CREATE INDEX,或者使用与索引相关的ALTER TABLE。禁止和激活索引有两种办法:

  · 你可用使用ALTER TABLE的DISABLE KEYS和ENABLE KEYS形式:

ALTER TABLE tbl_name DISABLE KEYS;
ALTER TABLE tbl_name ENABLE KEYS;

  这些语句关闭或打开表中非唯一(non-unique)索引的更新过程。

  ALTER TABLE的DISABLE KEYS和ENABLE KEYS子句是索引禁止和激活操作的推荐方法,因为服务器也是这样操作的(如果你使用LOAD DATA语句把数据载入空的MyISAM表中,服务器会自动地执行这样的优化操作)。

  · Myisamchk工具可以执行索引维护。它直接在数据表文件上进行操作,因此使用它的时候,你必须拥有数据表文件的写入权限。
使用myisamchk禁止MyISAM表的索引的方法是,首先你要确保已经告诉了服务器让该数据表独立出来,接着把它移动到适当的数据库目录中,并运行下面的命令:

% myisamchk –keys-used=0 tbl_name

  载入数据之后,重新激活索引:

% myisamchk –recover –quick –keys-used=n tbl_name

  其中的n是位掩码(bitmask),它指明了要激活的索引。Bit 0(第一个位)与索引1对应。例如,如果某张表拥有三个索引,那么n的值应该是7(二进制的111)。你也可以使用–description选项来检测索引的数量:

% myisamchk –description tbl_name

  前面的数据载入原则也可以应用于混合查询环境(客户端执行多种不同的操作)。例如,你应该避免在那些频繁被修改(写入)的数据表上运行长时间的SELECT查询。这会引发大量的争用(contention),导致写入操作的性能较差。一个可能的解决办法是,如果你的写入操作主要是INSERT操作,那么把新记录添加到辅助表中,接着周期性地把这些记录添加到主表中。如果你必须立即访问这些新记录,那么这个策略是不行的,但是如果你能够承担得起短期内不访问这些数据的代价,那么使用辅助表可以在两个方面带来好处。首先,它减少了主表上的SELECT查询争用的问题,因此它们执行得更快。其次,把辅助表中的批量数据载入主表中所花费的时间总和也比单独载入记录花费的时间总和要小一些;键缓存只需要在每次批量载入结束后刷新一次,而不用每个数据行载入后都刷新一次。

  使用这种策略的一个应用是把Web服务器的Web页面访问日志载入MySQL数据库的时候。在这种情况下,保证实体立即进入主表的优先级并不高(没有这个必要性)。

  如果你在MyISAM表上使用了混合的INSERT和SELECT语句,你就可以利用并发性插入操作的优点了。这个特性允许插入和检索操作同时进行,而不需要使用辅助表。你可以查看”使用并发性插入操作”部分。

JDBC性能优化的三大技巧

JDBC性能优化的三大技巧

本系列的性能提示将为改善JDBC应用程序的性能介绍一些基本的指导原则,这其中的原则已经被许多现有的JDBC应用程序编译运行并验证过。 这些指导原则包括:

 正确的使用数据库MetaData方法

   只获取需要的数据

   选用最佳性能的功能

   管理连接和更新

  以下这些一般性原则可以帮助你解决一些公共的JDBC系统的性能问题.

  使用数据库Metadata方法

  因为通过ResultSet对象生成的Metadata方法与其它的JDBCB方法相比是较慢的, 经常的使用它们将会削弱系统的的性能. 本节的指导原则将帮助你选择和使用meatdata时优化系统性能.

  少用Metadata方法

  与其它的JDBC方法相比, 由ResultSet对象生成的metadata对象的相对来说是很慢的. 应用程序应该缓存从ResultSet返回的metadata信息,避免多次不必要的执行这个操作.

  几乎没有哪一个JDBC应用程序不用到metadata,虽然如此,你仍可以通过少用它们来改善系统性能. 要返回JDBC规范规定的结果集的所有列信息, 一个简单的metadata的方法调用可能会使JDBC驱动程序去执行很复杂的查询甚至多次查询去取得这些数据. 这些细节上的SQL语言的操作是非常消耗性能的.

  应用程序应该缓存这些metadata信息. 例如, 程序调用一次getTypeInfo方法后就将这些程序所依赖的结果信息缓存. 而任何程序都不大可能用到这些结果信息中的所有内容,所以这些缓存信息应该是不难维护的.

  避免null参数

  在metadata的方法中使用null参数或search patterns是很耗时的. 另外, 额外的查询会导致潜在的网络交通的增加. 应尽可能的提供一些non-null的参数给metadata方法.

  因为metadata的方法很慢, 应用程序要尽可能有效的调用它们. 许多应用程序只传递少量的non-null参数给这些方法.

  例如:

ResultSet WSrs = WSc.getTables (null, null, “WSTable”, null);

  应该这样:

ResultSet WSrs = WSc.getTables (“cat1”, “johng”, “WSTable”, “TABLE”);

  在第一个getTables()的调用中, 程序可能想知道表’WSTable’是否存在. 当然, JDBC驱动程序会逐个调用它们并且会解译不同的请求. JDBC驱动程序会解译请求为: 返回所有的表, 视图, 系统表, synonyms, 临时表, 或存在于任何数据库类别任何Schema中的任何别名为’WSTable’的对象.

  第二个getTables()的调用会得到更正确的程序想知道的内容. JDBC驱动程序会解译这个请求为: 返回当前数据库类别中所有存在于’johng’这个schema中的所有表.

  很显然, JDBC驱动程序处理第二个请求比处理第一个请求更有效率一些.

  有时, 你所请求信息中的对象有些信息是已知的. 当调用metadata方法时, 程序能传送到驱动程序的的任何有用信息都可以导致性能和可靠性的改善.

  使用’哑元'(dummy)查询确定表的特性

  要避免使用getColumns()去确定一个表的特性. 而应该使用一个‘哑元’查询来使用getMetadata()方法.

  请考虑这样一个程序, 程序中要允许用户选取一些列. 我们是否应该使用getColumns()去返回列信息给用户还是以一个’哑元’查询来调用getMetadata()方法呢 

  案例 1: GetColumns 方法

ResultSet WSrc = WSc.getColumns (… “UnknownTable” …);

// getColumns()会发出一个查询给数据库系统

. . .

WSrc.next();

string Cname = getString(4);

. . .

// 用户必须从反复从服务器获取N行数据

// N = UnknownTable的列数

  案例 2: GetMetadata 方法

// 准备’哑元’查询

PreparedStatement WSps = WSc.prepareStatement

(“Select * from UnknownTable Where 1 = 0”);

// 查询从来没有被执行,只是被预储

ResultSetMetaData WSsmd=WSps.getMetaData();

int numcols = WSrsmd.getColumnCount();



int ctype = WSrsmd.getColumnType(n)



// 获得了列的完整信息 

  在这两个案例中, 一个查询被传送到服务器. 但在案例1中, 查询必须被预储和执行, 结果的描述信息必须确定(以传给getColumns()方法), 并且客户端必须接收一个包含列信息的结果集. 在案例2中, 只要准备一个简单的查询并且只用确定结果描述信息. 很显然, 案例2执行方式更好一些.

  这个讨论有点复杂, 让我们考虑一个没有本地化支持prepared statement的DBMS服务器. 案例1的性能没有改变, 但案例2中, 因为’哑元’查询必须被执行而不是被预储使得它的性能增强了一些. 因为查询中的Where子句总是为FALSE, 查询在不用存取表的数据情况的下会生成没有数据的结果集. 在这种情况下,第二种方式当然比第一种方式好一些.

  总而言之,总是使用ResultSet的metadata方法去获取列信息,像列名,列的数据类型,列的数据精度和长度等. 当要求的信息无法从ResultSet的metadata中获取时才去用getColumns()方法(像列的缺省值这些信息等)。

本篇文章来源于 :刘志猛博客 原文链接:http://www.liuzm.com/article/mysql/9614a.htm

http://www.liuzm.com

MySQL同步(五)

译者:叶金荣,来源:MySQL手册版本 5.0.20,转载请注明译者和出处,并且不能用于商业用途,违者必究。

6.9 同步 FAQ

 

 

: master还在运行中,如何在不停止它的情况下配置slave?

 

 

: 需要设计几个选项参数。如果已经有了master的备份并且记录了数据快照二进制日志文件名以及偏移位置(运行 SHOW MASTER STATUS 查看结果),执行以下步骤:

 

  1. 确定slave指定了一个唯一的服务器编号。

     

     

  2. 在slave上执行如下语句,把一些选项值改成实际值:

     

    mysql> CHANGE MASTER TO
        ->     MASTER_HOST='master_host_name',
        ->     MASTER_USER='master_user_name',
        ->     MASTER_PASSWORD='master_pass',
        ->     MASTER_LOG_FILE='recorded_log_file_name',
        ->     MASTER_LOG_POS=recorded_log_position;  

     

  3. 在slave上执行 START SLAVE 语句。

 

 

如果事先没有备份master的数据,可以用以下方法快速创建一个备份。以下所有的操作都是在master上。

 

  1. 提交语句:

     

    mysql> FLUSH TABLES WITH READ LOCK;
    

     

  2. 确保这个锁一直存在,执行以下命令(或者其他类似的):

     

    shell> tar zcf /tmp/backup.tar.gz /var/lib/mysql
    

     

  3. 执行以下语句,记录下输出的结果,后面要用到:

     

    mysql> SHOW MASTER STATUS;
    

     

  4. 释放锁:

     

    mysql> UNLOCK TABLES;

     

 

 

上述步骤的另一个办法是创建master的SQL转储文件。只需在master上执行 mysqldump --master-data 命令,然后将导出来的SQL转储文件载入slave。不过,这么做会制作二进制数据快照的方式慢一点。

 

无论使用上述两种方法的哪种,最后都能创建master的数据快照然后记录二进制日志文件名以及偏移位置。可以在好几的其他的slave上使用同一个备份的二进制数据快照。得到master的快照后,只要master的二进制日志完好无损,接着就能开始设置slave了。两个决定是否需要等待较长时间的限制是:在master上磁盘空间保存二进制日志,以及slave从master抓取更新事件。

 

也可以使用 LOAD DATA FROM MASTER。这个语句可以很方便地在slave上取得数据快照并且能立刻调整二进制日志文件名以及偏移位置。在将来,我们推荐用 LOAD DATA FROM MASTER 来设置slave。警告,它只能用于 MyISAM 表,并且可能会保持一个较长时间的读锁。由于它还没达到所期望的高效率,因此如果数据表很大,最好还是在执行完 FLUSH TABLES WITH READ LOCK 后直接制作二进制数据快照。

 

:是否slave总是需要连接到master?

 

:不,非必需。slave可以好几小时甚至几天关闭或者不连接master,然后重连再取得更新操作日志。例如,可以在拨号链接上设置一个mater/slave关系,拨号可能只是零星的不定期的连接。这种做法隐含的是,在任何指定的时间里,除非使用特殊的度量标准,否则slave不能保证总是能和master保持同步。在未来,有个选项可以阻止master,除非至少有一个slave在同步中。

 

:怎么知道比master晚了多少?也就是说,怎么知道slave最后同步的时间?

 

:如果slave是4.1.1或者更高,只需查看 SHOW SLAVE STATUS 结果中的 Seconds_Behind_Master 字段。对于老版本,可以用以下办法。如果在slave上执行 SHOW PROCESSLIST 语句结果显示SQL线程(对MySQL 3.23则是slave线程)正在运行,这就意味着该线程至少从master读取一个更新操作事件。详情请看”6.3 Replication Implementation Details“。

 

当SQL线程执行一个master上读取的更新操作事件时,它把自己的时间改成事件的时间(这也就是 TIMESTAMP 也要同步的原因)。在
SHOW PROCESSLIST 结果中的 Time 字段中,slave的SQL线程显示的秒数就是最后一次同步的时间戳和slave本机的实际时间相差秒数。可以根据这个值来判断最后同步的时间。注意,如果slave已经从master断开好几个小时了,然后重新连接,就能看到slave的
SHOW PROCESSLIST 结果中的SQL线程的Time 字段的值类似3600。这是因为slave正在执行一个小时前的语句。

 

:如何强制master在slave赶上全部更新之前阻止更新操作?

 

:执行以下步骤:

 

  1. 在master上,执行以下语句:

     

    mysql> FLUSH TABLES WITH READ LOCK;
    mysql> SHOW MASTER STATUS;  

    记录下结果中的日志文件名以及偏移位置,它们是同步的坐标值。

  2. 在slave上,提交以下语句,MASTER_POS_WAIT() 函数的参数的值就是前面取得的同步坐标值:

     

    mysql> SELECT MASTER_POS_WAIT('log_name', log_offset);
    

    SELECT 语句会阻止更新,直到slave同步到了上述日志文件及位置。在这个时候,slave就和master保持同步了,并且这个语句就会返回。

     

  3. 在master上,执行以下语句允许master重新处理更新操作:

     

    mysql> UNLOCK TABLES;

     

 

 

:设置一个双向复制时要注意什么问题?

 

:MySQL同步目前还不支持任何在master和slave上的分布式(跨服务器)更新锁协议以保证操作的原子性。也就是说,存在这样的可能性:客户端A在并存的master 1上做了一个更新,同时,在它同步到并存master 2上之前,客户端B在master 2上可能也做了一个和客户端A在master 1上不同的更新操作。因此,当客户端A所做的更新同步到master 2时,它将产生和master 1上不同的数据表,尽管master 2上的更新操作也全都同步到master 1上去。这意味着除非能确保所有的更新都能以任何顺序安全地执行,否则不要使用双向同步,或者除非注意在客户端程序中的不知原因的无序更新操作。

 

同时也要意识到在所关心的更新问题上,双向同步实际上并不能很大地改善性能(甚至没有)。两个服务器都需要执行同样数量的更新操作,在一台服务器上也是。唯一区别的是,可能这样做会减少一些锁争夺,因为来自其他服务器的更新操作都会被串行地放到slave线程中。甚至这种好处还可以作为网络延迟的补偿。

 

:我如何利用同步来提高系统性能?

 

:需要安装一个服务器作为master并且把所有的写操作直接放在这上面。然后配置多个廉价的使用机架磁盘的slave,把读操作分配给master和slave。还可以在启动slave时使用 --skip-innodb, --skip-bdb, --low-priority-updates,和 --delay-key-write=ALL 选项来提高slave端的性能。这种情况下,slave会使用非事务的 MyISAM 表来代替 InnoDBBDB 表,已取得更快速度。

 

:如何准备客户端应用程序的代码来适应同步应用?

 

:如果代码中负责存取数据库的部分已经被合理地抽象化/模块化了,将它们转化成适用运行于同步环境中将会很平滑和简单。只需要修改数据库存取实现部分,把所有的写操作放到master上,把所有的读操作放到master或者slave上。如果你的代码还没达到这个层次的抽象化,那么这将成为整理代码的机会和动机。可以使用类似以下函数创建封装类库或者模块:

 

  •  

    safe_writer_connect()

  •  

    safe_reader_connect()

  •  

    safe_reader_statement()

  •  

    safe_writer_statement()

 

 

每个函数名的 safe_ 表示它们会处理所有的错误情况。可以使用其他函数名。重要的是,要为读连接、写连接、读、写定义好统一的接口。

 

然后将客户端代码转换成使用封装的类库。已开始可能是很痛苦且麻烦的,不过在将来长期运行中就能得到回报了。所有使用上述方法的应用程序都会在master/slave配置中有优势,即使包含多个slave。这些代码将很容易维护,一些额外的麻烦也会很少。自豪需要修改一个或者两个函数;例如,想要记录每个语句执行了多长时间,或者在上千个语句中哪个出现错误了。

 

如果已经写了很多代码,你可能想要自动转换它们,那么可以使用MySQL发布的 replace 工具,或者自己写转换脚本。理想地,你的代码已经使用了统一的编程风格。如果不是,最好重写它们,或者可以遍历检查一下,手工规范化一下代码风格。

 

:MySQL同步何时且有多少能提高系统性能?

 

:MySQL同步对于频繁读但不频繁写的系统很有好处。理论上来讲,使用单一master/多slave的配置,就可以通过这个方法来衡量系统:增加更多的slave直到用完所有的网络带宽或者master的更新操作增长到了不能再处理的点了。

 

想要知道增加多少个slave之后得到的性能才能平稳,以及能提高多少性能,就需要知道查询模式,并且根据经验对典型的master和slave做读(每秒读或 max_reads)和写(max_write)基准测试得到它们之间的关系。下例展示了一个理想系统取得的性能的简单计算方法。

 

设定系统负载由10%写和90%读组成,我们已经通过基准测试确定 max_reads 是1200 – 2 * max_writes。换句话说,系统可以达到每秒做没有写的1200次读操作,写操作平均是读操作的2倍慢,它们之间的关系是线性的。让我们假设master和每个slave都有同样的容量,有一个master和N个slave。每个服务器(master或slave):

 

 

reads = 1200 - 2 * writes

 

reads = 9 * writes / (N + 1) (读是分开的,但是所有写是在所有的服务器上的)

 

9 * writes / (N + 1) + 2 * writes = 1200

 

 

writes = 1200 / (2 + 9/(N+1))

 

最后的等式说明了N个slave的最大写数量,给它每分钟的最高读频率1200和1次写9次读的机率。

 

分析结论比率如下:

 

  • 如果 N = 0(意味着没有同步),系统大致可以处理每秒 1200/11 = 109 次写。

     

     

  • 如果 N = 1,增加到每秒 184 次写。
  • 如果 N = 8,增加到每秒 400 次写。

     

     

  • 如果 N = 17,增加到每秒 480 次写。

     

     

  • 最终,随着N接近无穷大(我们的预算为负无穷大),则可以达到几乎每秒 600 次写,大约提高系统吞吐量 5.5 倍。尽管如此,当有8台服务器时,已经提高了4倍了。

 

 

注意,上面的计算是假设了网络带宽无穷大,并且忽略了一些系统中比较大的因素。在很多情况下,当系统增加 N 个同步slave之后,是无法精确计算出上述预计结果的。不过,先看看下列问题将有助于你知道是否有和有多少系统性能上的改善:

 

  • 系统读/写得比率是多少?

     

     

  • 减少读操作后一个服务器能增加处理多少写操作?

     

     

  • 你的网络带宽足够给多少slave使用?

 

 

:如何利用同步提供冗余/高可用性?

 

:使用当前已经可用的特性,可以配置一个master和一个(或多个)slave,并且写一个脚本监控master是否运行着。然后通知应用程序和slave在发现错误时修改master。一些建议如下:

 

  • 使用 CHANGE MASTER TO 语句告诉slave修改master。

     

     

  • 一个让应用程序定位master所在主机的办法就是给master使用动态DNS。例如bind就可以用 `nsupdate` 来动态更新DNS。
  •  

    使用 --log-bin 选项,不使用
    --log-slave-updates 选项来启动slave。这样就能让slave运行 STOP SLAVE; RESET MASTER 语句后随时准备变成master,并且在其他slave上运行
    CHANGE MASTER TO。例如,有以下配置方案:

           WC
            \
             v
     WC----> M
           / | \
          /  |  \
         v   v   v
        S1   S2  S3
    

    M 表示masetr,S 表示slave,WC表示提交读写操作的客户端;只提交读操作的客户端没有表示出来,因为它们无需切换。S1,S2,S3都是使用
    --log-bin 选项,不用 --log-slave-updates 选项运行的slave。由于除非指定 --log-slave-updates 参数,否则从master读到的更新操作都不会记录到二进制日志中,因此每个slave上的二进制日志都是空的。如果因为某些原因 M 不能用了,可以指定一个slave作为master。例如,如果指定S1,则所有的WC都要重定向到S1上,S2和S3都需要从S1上同步。

    确定所有的slave都已经处理完各自的中继日志了。在每个slave上,提交 STOP SLAVE IO_THREAD 语句,然后检查 SHOW PROCESSLIST 的结果直到看到 Has read all relay log 了。当所有的slave都这样子之后,就可以按照新的方案设置了。在slave S1上提交

    STOP SLAVERESET MASTER 语句将其提升为master。

    在其他slave S2和S3上,提交 STOP SLAVECHANGE MASTER
    TO MASTER_HOST='S1'
    ( 'S1' 代表S1的真实主机名) 语句修改master。把S2,S3如何连接到S1的参数(用户,密码,端口等)都附加到 CHANGE MASTER 后面。在
    CHANGE MASTER 中无需指定S1的二进制日志文件名和偏移位置:因为 CHANGE MASTER 默认就是第一个二进制日志和偏移位置4。最后,在S2和S3上提交 START SLAVE 语句。

    然后让所有的WC都把他们的语句重定向到S1上。从这个时候开始,从所有的WC发送到S1上的更新语句都会写到S1的二进制日志中,它们包含了从M死掉之后发送到S1的全部更新语句。

    配置结果如下:

           WC
          /
          |
     WC   |  M(unavailable)
      \   |
       \  |
        v v
         S1<--S2  S3
          ^       |
          +-------+
        

    当M又起来了之后,只需在M上提交和在S2和S3上的一样的 CHANGE MASTER 语句,将它变成一个slave并且读取自从它死掉之后的全部WC提交的更新操作。想要把M重新变成master(例如因为它的性能更好),就执行类似上面的操作,把S1当作失效了,把M提升为新的master。在这个步骤中,别忘了在把S2和S3修改成为M的slave之前在M上运行 RESET MASTER 语句。否则的话,它们会从M开始失效的那个时刻开始读取WC提交的更新操作日志。

 

 

现在我们就运行着一个完整的自动选择master的MySQL同步系统,不过在它准备好之前,需要创建自己的监控工具。

 

6.10 同步疑难解答

 

 

如果按照上述步骤设定好同步之后,它不能正常工作的话,首先检查以下内容:

 

  • 查看一下错入日志信息。不少用户都在这方面做得不够好以至于浪费时间。

     

     

  • master是否在记录二进制日志?用 SHOW MASTER STATUS 检查一下状态。如果是,Position 的值不为零;否则,确定master上使用了 log-binserver-id 选项。
  • slave是否运行着?运行 SHOW SLAVE STATUS 语句检查

     

    Slave_IO_RunningSlave_SQL_Running 的值是否都是。如果不是,确定是否用同步参数启动slave服务器了。

  • 如果slave正在运行,它是否建立了到master的连接?运行
    SHOW PROCESSLIST 语句检查I/O和SQL线程的 State 字段值。详情请看”6.3 Replication Implementation Details“。如果I/O线程状态为 Connecting to master,就检查一下master上同步用户的权限是否正确,master的主机名,DNS设置,master是否确实正在运行着,以及slave是否可连接到master,等等。
  • 如果slave以前运行着,但是现在停止了,原因通常是一些语句在master上能成功但在slave上却失败了。如果salve已经取得了master的全部快照,并且除了slave线程之外不会修改他的数据,那么应该不会发生这样的情形。如果确实发生了,那么可能是一个bug或者你碰到了”6.7 Replication Features and Known Problems“中提到的同步限制之一。如果是一个bug,那么请按照”6.11 Reporting Replication Bugs“的说明报告它。
  • 如果一个语句在master上成功了,但是在slave上却失败了,并且这时不能做一次完整的数据库再同步(也就是删除slave上的数据,重新拷贝master的快照),那么试一下:
    1. 判断slave的数据表是否和master的不一样。试着找到怎么会发生这种情况,然后将slave的表同步成和master一样之后运行 START SLAVE

       

       

    2. 如果上述步骤不生效或者没有执行,试着这个语句是否能被手工安全地运行(如果有必要),然后忽略master的下一个语句。
    3. 如果决定要忽略master的下一个语句,只需在slave上提交以下语句:

       

      mysql> SET GLOBAL SQL_SLAVE_SKIP_COUNTER = n;
      mysql> START SLAVE;

      如果下一个语句没有使用 AUTO_INCREMENTLAST_INSERT_ID(),那么 n 的值应为为 1。否则,它的值为 2。设定为 2 是因为 AUTO_INCREMENTLAST_INSERT_ID() 在master的二进制日志中占用了2条日志。

       

    4. 如果确定slave精确地同步master了,并且没有除了slave线程之外的对数据表的更新操作,则推断这是因为bug产生的差异。如果是使用最近的版本,请报告这个问题,如果使用的是旧版本,试着升级一下。

 

 

6.11 报告同步Bugs

 

 

当确定没有包含用户的错误,并且同步还是不能正常工作或者不稳定,就可以报告bug了。我们需要你尽量多的跟踪bug的信息。请花点时间和努力准备一个好的bug报告。

 

如果有一个演示bug的可重现测试案例的话,请进入我们的bug数据库http://bugs.mysql.com/。如果碰到一个幽灵般的问题(不可重现),请按照如下步骤:

 

  1. 确定没有包含用户错误。例如,在slave线程以外更新slave的数据,那么数据就会不能保持同步,也可能会导致违反更新时的唯一键问题。这是外部干涉导致同步失败的问题。
  2. 使用 --log-slave-updates--log-bin 选项启动slave。这会导致slave将从master读取的更新操作写到自己的二进制日志中。
  3. 在重设同步状态之前保存所有的证据。如果我们没有任何信息或者只有粗略的信息,这将很难或者不可能追查到这个问题。需要收集以下证据:

    • master上的所有二进制日志
    • slave上的所有二进制日志
    • 发现问题时,在master上执行 SHOW MASTER STATUS 的结果
    • 发现问题时,在master上执行 SHOW SLAVE STATUS 的结果
    • 记录master和slave的错误日志

  4. mysqlbinlog 来检查二进制日志。例如,用以下方法有助于找到有问题的查询:

     

    shell> mysqlbinlog -j pos_from_slave_status \
               /path/to/log_from_slave_status | head
    

     

 

一旦收集好了问题的证据,首先将它隔离到一个独立的测试系统上。然后在我们的bug数据库http://bugs.mysql.com/上进可能详细地报告问题。

MySQLReference(2)

6.3.6.2 辅助功能函数

DATABASE()

返回当前数据库名:

mysql> SELECT DATABASE();

        -> ‘test’

如果没有当前数据库,DATABASE() 返回一个空字符串。

USER()

SYSTEM_USER()

SESSION_USER()

返回当前 MySQL 用户名:

mysql> SELECT USER();

        -> ‘davida@localhost’

在 MySQL 3.22.11 或更新的版本中,返回值包含用户名和客户机的主机名。你可以象下面所示的仅取出用户名部分(无论值是否包含一个主机名部分,它均能正常工作):

mysql> SELECT SUBSTRING_INDEX(USER(),”@”,1);

        -> ‘davida’

CURRENT_USER()

返回当前会话被验证匹配的用户名:

mysql> SELECT USER();

        -> ‘davida@localhost’

mysql> SELECT * FROM mysql.user;

        -> ERROR 1044: Access denied for user: ‘@localhost’ to database ‘mysql’

mysql> SELECT CURRENT_USER();

        -> ‘@localhost’

PASSWORD(str)

OLD_PASSWORD(str)

从纯文本口令 str 计算一个口令字符串。这个函数用于对存储到授权表 user 的Password 列中的 MySQL 口令进行加密。

mysql> SELECT PASSWORD(‘badpwd’);

        -> ‘7f84554057dd964b’

PASSWORD() 加密是不可逆的。 PASSWORD() 不以与 Unix 口令加密相同的方式进行口令加密。参见 ENCRYPT()。 注意, PASSWORD() 函数是用于在 MySQL 服务中验证系统的,你不应该 在你的应用程序中使用它。你可以使用 MD5() 或 SHA1() 代替使用它。同样查看 RFC-2195 可获得有关应用程序的口令处理与安全验证的更多信息。

ENCRYPT(str[,salt])

Encrypt使用 Unix crypt() 系统调用加密 str 。参数 salt 应该是一个有两个字符的字符串,(在 MySQL 3.22.16 中,salt 可以超过两个字符。):

mysql> SELECT ENCRYPT(“hello”);

        -> ‘VxuFAJXVARROc’

如果 crypt() 在你的系统上不可用,ENCRYPT() 总是返回 NULL。 ENCRYPT() 只保留 str 中前 8 个字符,而忽略其它所有的,至少在某些系统上是这样的。这取决于底层 crypt() 系统调用的行为。

ENCODE(str,pass_str)

使用 pass_str 做为密钥加密 str。使用 DECODE() 解密结果。结果是一个与 string 一样长的二进制字符。如果希望将它保存到一个列中,请使用 BLOB 列类型。

DECODE(crypt_str,pass_str)

使用 pass_str 作为密钥解密加密后的字符串 crypt_str。crypt_str 应该是一个由 ENCODE() 返回的字符串。

MD5(string)

计算一个字符串的 MD5 128 位校验和。值作为一个 32 位的十六进制数字返回,例如,被用于一个哈希(hash)键:

mysql> SELECT MD5(“testing”);

        -> ‘ae2b1fca515949e5d54fb22b8ed95575’

这是 “RSA 数据安全公司的 MD5 消息-摘要算法”。

SHA1(string)

SHA(string)

计算一个字符串的 SHA1 160 位校验和(在 RFC 3174 (Secure Hash Algorithm) 中被描述)。返回值是一个 40 位的十六进制数字,或在输入参数为 NULL 的情况下,返回值为 NULL。一个使用这个函数的可能就是用于一个哈希键。你也可以使用它作为存储密码时的密码安全函数。

mysql> SELECT SHA1(“abc”);

        -> ‘a9993e364706816aba3e25717850c26c9cd0d89d’

SHA1() 在MySQL 4.0.2 中被加入,并可被当做比 MD5() 加密更安全的等价物。SHA() 是 SHA1() 的同义词。

AES_ENCRYPT(string,key_string)

AES_DECRYPT(string,key_string)

这些函数允许使用官方的 AES(Advanced Encryption Standardadvanced 先进的密码技术标准) 算法加密/解密数据。加密时使用 128 位长的密钥,但是你可以通过修改源码将其扩展到 256 位。我们选择 128 位是因为它更快一点并且已足够安全了。 输入的参数可以是任意长度的。如果任何一个参数是 NULL,这个函数返回值也将是 NULL。 因为 AES 是一个块级的算法,加密不同长度的字符串时会对其进行填充,因而结果字符串的长度也可以通过 16*(trunc(string_length/16)+1) 计算出。 如果 AES_DECRYPT() 发现数据无效或错误的填料,它将返回 NULL。可是,如果输入的数据或密钥是无效的,函数仍然可能返回一个非 NULL 值 (可能是无用的信息)。 通过修改你的 SQL 语句,你可以使用 AES 函数以一个加密的形式存储数据:

INSERT INTO t VALUES (1,AES_ENCRYPT(“text”,”password”));

尽量避免在一个连接上的每个查询中传递密钥,这样可以得到更高的安全性,上述方式可以通过连接时在服务器端存储密钥来完成:

SELECT @password:=”my password”;

INSERT INTO t VALUES (1,AES_ENCRYPT(“text”,@password));

AES_ENCRYPT() 和 AES_DECRYPT() 在 MySQL 4.0.2 中被加入,可以被考虑为当前 MySQL 中可用的加解密函数中最密码安全的。

DES_ENCRYPT(string_to_encrypt [, (key_number | key_string) ] )

使用 Triple-DES 算法以给定的密钥对字符串加密。 注意,只有配置了 MySQL 对 SSL 的支持,这个函数才能正常工作。查看章节 4.3.9 使用安全地连接。 编码密钥以下列各项方法选择: 参数  含义 

只有一个参数  des-key-file 中的第一个密钥被使用。 

key number  des-key-file 中给定的密钥 (0-9) 被使用。 

string  给定的 key_string 将被用于加密 string_to_encrypt。 

返回字符串是一个二进制字符串,并且第一个字符是 CHAR(128 | key_number)。 128 被加入是为了更加容易地识别一个加密密钥。如果你使用一个字符串密钥,key_number 将是 127。 当发生错误时,这个函数返回 NULL。 返回字符串的长度将为:new_length= org_length + (8-(org_length %)+1。 des-key-file 的格式如下:

key_number des_key_string

key_number des_key_string

每个 key_number 必须是一个在 0 到 9 范围之内的数字。文件中的行可以是任何次序的。des_key_string 是用于加密消息的字符串。在数字与密钥之间至少要有一个空格。如果你没在 DES_ENCRYPT() 指定任何密钥参数,那么文件中的第一个密钥将被缺省使用。 以 FLUSH DES_KEY_FILE 命令,你可以告诉 MySQL 从密钥文件中读取新的密钥值。这个操作需要你有 Reload_priv 权限。 有一套默认密钥的一个好处就是,它给应用程序一个检查存在的加密列值的方法,而不需要给最终用户解密这些值的权限。

mysql> SELECT customer_address FROM customer_table WHERE

       crypted_credit_card = DES_ENCRYPT(“credit_card_number”);

DES_DECRYPT(string_to_decrypt [, key_string])

解密 DES_ENCRYPT() 加密后的字符串。 注意,只有配置了 MySQL 对 SSL 的支持,这个函数才能正常工作。查看章节 4.3.9 使用安全地连接。 如果 key_string 参数没有给出,DES_DECRYPT() 检查加密字符串的第一个字节,以确定用于加密原始字符串的 DES 密钥数字,然后从 des-key-file 读取密钥用于解密消息。为了能这样工作,该用户必须有 SUPER 权限。 如果将一个 key_string 参数传递给该函数,这个字符串将被作为解密消息的密钥。 如果 string_to_decrypt 看上去不像是一个加密字符串,MySQL 将返回给定的 string_to_decrypt。 当发生错误时,该函数返回 NULL。

LAST_INSERT_ID([expr])

返回被插入到一个 AUTO_INCREMENT 列中的最后一个自动产生的值。查看章节 8.1.3.130 mysql_insert_id()。

mysql> SELECT LAST_INSERT_ID();

        -> 195

最后产生的 ID 是以每个连接为基础在服务器端被维护的。它不可能被其它的客户端连接改变。如果你以一个非特殊值(即一个非 NULL 和非 0 的值)更新其它的 AUTO_INCREMENT,它甚至也不会改变。 如果你在同一时间内以一个插入语句插入了许多记录行,LAST_INSERT_ID() 将返回第一个被插入行的值。这样做的原因是因为,这可能列容易地在其它服务器上再现同一条 INSERT 语句。 如果 expr 被作为一个参数传递给 LAST_INSERT_ID(),那么函数将返回这个参数的值,并且被设置为 LAST_INSERT_ID() 返回的下一个值。这可被用于模拟一个序列: 首先创建一个表:

mysql> CREATE TABLE sequence (id INT NOT NULL);

mysql> INSERT INTO sequence VALUES (0);

然后这个表可以被用来以下面的方式产生序列值:

mysql> UPDATE sequence SET id=LAST_INSERT_ID(id+1);

你也可以不调用 LAST_INSERT_ID() 产生序列,但是以这种方式使用这个函数的作用就是,ID 值是在服务器端叙谈最后的自动产生值被维护的(是多用户安全的)。你可以检索这的新的 ID 值,就好像读取 MySQL 中任何正常的 AUTO_INCREMENT 值一样。举例来说,LAST_INSERT_ID()(无任何参数) 将返回一个新的 ID。C API 函数 mysql_insert_id() 也可以用来得到这个值。 注意,由于 mysql_insert_id() 仅仅只能用于在 INSERT 和 UPDATE 语句的更新之后,所以在执行了其它的 SQL 语句(比如 SELECT 或 SET)之后,你不能够使用 C API 函数检索到 LAST_INSERT_ID(expr) 的值。

FORMAT(X,D)

将数字 X 格式化为一个 ‘#,###,###.##’ 的形式,四舍五入到 D 位小数。如果 D 为 0,返回的结果将没有小数点和小数部分:

mysql> SELECT FORMAT(12332.123456, 4);

        -> ‘12,332.1235’

mysql> SELECT FORMAT(12332.1,4);

        -> ‘12,332.1000’

mysql> SELECT FORMAT(12332.2,0);

        -> ‘12,332’

VERSION()

以一个字符串形式返回 MySQL 服务器的版本:

mysql> SELECT VERSION();

        -> ‘3.23.13-log’

注意,如果你的版本以 -log 结果,那就意味着日志是被设为可用的。

CONNECTION_ID()

返回当前连接的连接 ID(thread_id)。每个连接均有一个自己唯一的 id:

mysql> SELECT CONNECTION_ID();

        -> 1

GET_LOCK(str,timeout)

尝试获得一个由字符串 str 给定名字的锁定与一个 timeout 秒的超时。如果锁定被获得成功,返回 1,如果尝试超时,返回 0,或者一个错误发生(比如内存溢出或线程被 mysqladmin kill 杀死),返回NULL。当你执行 RELEASE_LOCK()、执行一个新的 GET_LOCK(),或线程终止时,一个锁定被释放。这个函数可以被用于执行应用程序锁定或模拟记录锁定。它会阻塞其它的客户端用同样的名字的锁定请求;遵从一个给定锁定字符串名的客户端可以使用这个字符串来执行子协作建议的锁定:

mysql> SELECT GET_LOCK(“lock1”,10);

        -> 1

mysql> SELECT IS_FREE_LOCK(“lock2”);

        -> 1

mysql> SELECT GET_LOCK(“lock2”,10);

        -> 1

mysql> SELECT RELEASE_LOCK(“lock2”);

        -> 1

mysql> SELECT RELEASE_LOCK(“lock1”);

        -> NULL

注意,第二个 RELEASE_LOCK() 调用返回 NULL 是因为 “lock1” 锁定被第二个 GET_LOCK() 调用自动地释放了。

RELEASE_LOCK(str)

释放由字符串 str 命名的通过 GET_LOCK() 获得的锁定。如果锁定被释放,返回 1;如果锁定并没有被当前线程锁定(在这种情况下,锁定不会被释放),返回 0;如果命名的锁定不存在,返回 NULL。如果锁定从来就没有通过调用一个 GET_LOCK() 获得,或已被释放了,那么该锁定将不存在。 DO 语句通常与 RELEASE_LOCK() 一起使用。查看章节 6.4.10 DO 句法。

IS_FREE_LOCK(str)

检查以 str 命名的锁定是否可以自由使用(也就是说,还未锁定)。如果锁定被释放了(没有一个人使用这个锁定),返回 1;如果这个锁定处于使用中,返回 0;如果发生一个错(例如错误的参数),返回 NULL。

BENCHMARK(count,expr)

BENCHMARK() 函数用于将表达式 expr 重复运行 count 次。它可以被用于计时 MySQL 处理表达式有多快。结果通常为 0。在 mysql 客户端有意使用它时,它将返回查询执行所需的时间:

mysql> SELECT BENCHMARK(1000000,ENCODE(“hello”,”goodbye”));

+———————————————-+

| BENCHMARK(1000000,ENCODE(“hello”,”goodbye”)) |

+———————————————-+

|                                            0 |

+———————————————-+

1 row in set (4.74 sec)

报告的时间是客户端经过的时间,不是服务器端的 CPU 时间。执行 BENCHMARK() 多次可能是明智的,并注意服务器的负载来解释结果。

INET_NTOA(expr)

给定一个数字的网络地址 (4 或 8 字节),以一个字符串的形式返回点组表示的地址:

mysql> SELECT INET_NTOA(3520061480);

       ->  “209.207.224.40”

INET_ATON(expr)

以字符串的形式给定一个点组表示的网络地址,返回一个地址的数字值表示的整数。地址可以是 4 或 8 个字节的地址:

mysql> SELECT INET_ATON(“209.207.224.40”);

       ->  3520061480

产生的数字通常是以网络地址字节的顺序;例如,上面的数字是以 209*256^3 + 207*256^2 + 224*256 +40 被计算出来的。

MASTER_POS_WAIT(log_name, log_pos)

阻塞,只到从服务器到达(也就是说,已读取并应用了所有更新,一直到)主服务器上的日志中指定的位置。如果主服务器上的信息没有初始化,或如果参数错误,返回 NULL。如果从服务器没有运行,将阻塞并造作,只到它启动并到达或超过指定的位置。如果从服务器已超过指定的位置,立即返回。 如果 timeout (在 4.0.10 中新加入) 被指定,当等待 timeout 秒经过后,将放弃。timeout 必须大于 0;一个零或一个负值 timeout 意味着超时。返回值是到达日志指定位置所必须等待的日志事件的数量,或者在出错的情况下为 NULL,或者超过超时时间返回 -1。 这个命令有益于控制主从服务器的同步,但是最初是为了复制测试的方便而写的。

FOUND_ROWS()

返回最后一个 SELECT SQL_CALC_FOUND_ROWS … 命令如果没有以 LIMIT 进行限制结果时将返回记录行数。

mysql> SELECT SQL_CALC_FOUND_ROWS * FROM tbl_name

       WHERE id > 100 LIMIT 10;

mysql> SELECT FOUND_ROWS();

第二个 SELECT 将返回一个数字,它指示前一个没有被 LIMIT 子句限制的 SELECT 将返回多少行记录。注意,如果你使用 SELECT SQL_CALC_FOUND_ROWS …,MySQL 不得不计算所有的记录行到结果集中。然而,这与你不使用 LIMIT 相比是更快一点的,因为结果集不需要发送到客户端。 SQL_CALC_FOUND_ROWS 从 MySQL 4.0.0 开始可以被使用。

6.3.7 用于 GROUP BY 子句的函数

如果在一个没有包含 GROUP BY 子句的一个语句中使用聚合函数,它将等价于将所有的记录行分为一组。

COUNT(expr)

返回由一个 SELECT 语句检索出来的记录行中非 NULL 值的记录总数目:

mysql> SELECT student.student_name,COUNT(*)

    ->        FROM student,course

    ->        WHERE student.student_id=course.student_id

    ->        GROUP BY student_name;

COUNT(*) 在它返回检索出的记录行的数目上稍微有点不同,它不管记录行中是否包括 NULL 值。 如果 SELECT 语句从一个表中进行检索,没有检索其它的列,并且没有 WHERE 子句,那么 COUNT(*) 将被优化以便更快地返回值。示例如下:

mysql> SELECT COUNT(*) FROM student;

COUNT(DISTINCT expr,[expr…])

返回一个互不相同的非 NULL 的值的总数目:

mysql> SELECT COUNT(DISTINCT results) FROM student;

在 MySQL 中,通过给出一个表达式列表,可以得到不包含 NULL 的不同的表达式组合的数目。在 ANSI SQL 中,你可能不得不在 COUNT(DISTINCT …) 中拼接所有的表达式。

AVG(expr)

返回 expr 的平均值:

mysql> SELECT student_name, AVG(test_score)

    ->        FROM student

    ->        GROUP BY student_name;

MIN(expr)

MAX(expr)

返回 expr 的最小或最大值。MIN() 和 MAX() 可以接受一个字符串参数;在这种情况下,它们将返回最小或最大的字符串传下。查看章节 5.4.3 MySQL 如何使用索引。

mysql> SELECT student_name, MIN(test_score), MAX(test_score)

    ->        FROM student

    ->        GROUP BY student_name;

在 MIN()、MAX() 和其它的合计函数中,MySQL 通常列的字符串值比较 ENUM 和 SET 列,而不是字符串在集合中相对应的位置。这将会被修正。

SUM(expr)

返回 expr 的总和。注意,如果返回集中没有从我任何记录行,它将返回 NULL !

GROUP_CONCAT(expr)

完整句法如下:

GROUP_CONCAT([DISTINCT] expr [,expr …]

             [ORDER BY {unsigned_integer | col_name | formula} [ASC | DESC] [,col …]]

             [SEPARATOR str_val])

这个函数在 MySQL 4.1 中被加入。函数返回一个字符串结果,该结果由分组中的值连接组合而成:

mysql> SELECT student_name,

    ->        GROUP_CONCAT(test_score)

    ->        FROM student

    ->        GROUP BY student_name;

or

mysql> SELECT student_name,

    ->        GROUP_CONCAT(DISTINCT test_score

    ->                     ORDER BY test_score DESC SEPARATOR ” “)

    ->        FROM student

    ->        GROUP BY student_name;

在 MySQL 中,你可以得到表达式结合体的连结值。通过使用 DISTINCT 可以排除重复值。如果希望对结果中的值进行排序,可以使用 ORDER BY 子句。为了以倒序排序,可以在 ORDER BY 子句中用于排序的列名后添加一个 DESC (递减 descending) 关键词。缺省为升序;这也可以通过使用 ASC 关键词明确指定。 SEPARATOR 是一个字符串值,它被用于插入到结果值中。缺省为一个逗号 (“,”)。你可以通过指定 SEPARATOR “” 完全地移除这个分隔符。 在你的配置中,通过变量 group_concat_max_len 要以设置一个最大的长度。在运行时执行的句法如下:

SET [SESSION | GLOBAL] group_concat_max_len = unsigned_integer;

如果最大长度被设置,结果值被剪切到这个最大长度。 GROUP_CONCAT() 函数是一个增强的 Sybase SQL Anywhere 支持的基本 LIST() 函数。如果只有一个列,并且没有其它选项被指定,GROUP_CONCAT() 是向后兼容有极大限制的 LIST() 函数。 LIST() 有一个缺省的排序次序。

示例(译者注):

mysql> CREATE TABLE `ta` (

    ->   `id` smallint(5) unsigned NOT NULL default ‘0’,

    ->   `name` char(60) NOT NULL default ”,

    ->   KEY `id` (`id`)

    -> ) TYPE=MyISAM;

Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO `ta` VALUES(“1”, “a”),(“1”, “b”),

    ->     (“1”, “c”),(“1”, “d”),(“2”, “a”),

    ->     (“2”, “b”),(“2”, “c”),(“3”, “d”);

Query OK, 8 rows affected (0.03 sec)

Records: 8  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM `ta`;

+—-+——+

| id | name |

+—-+——+

|  1 | a    |

|  1 | b    |

|  1 | c    |

|  1 | d    |

|  2 | a    |

|  2 | b    |

|  2 | c    |

|  3 | d    |

+—-+——+

8 rows in set (0.00 sec)

mysql> SELECT `id`,

    ->   GROUP_CONCAT(`name`)

    ->   FROM `ta`

    ->   GROUP BY `id`;

+—-+———————-+

| id | GROUP_CONCAT(`name`) |

+—-+———————-+

|  1 | a c b d              |

|  2 | a c b                |

|  3 | d                    |

+—-+———————-+

3 rows in set (0.03 sec)

# SEPARATOR 缺省是一个空格而不是一个逗号

mysql> SELECT `id`,

    ->   GROUP_CONCAT(DISTINCT `name`

    ->                ORDER BY `name` DESC SEPARATOR “,”) AS Result

    ->   FROM `ta`

    ->   GROUP BY `id`;

+—-+———+

| id | Result  |

+—-+———+

|  1 | d,c,b,a |

|  2 | c,b,a   |

|  3 | d       |

+—-+———+

3 rows in set (0.00 sec)

* 以上结果在 MySQL 4.1 中测试

示例结束(译者注)

VARIANCE(expr)

返回 expr 的标准方差(standard variance)。这是对 ANSI SQL 的扩展(只有在 4.1 或更新的版本中可用)。

STD(expr)

STDDEV(expr)

返回 expr 的标准偏差(standard deviation)。这是对 ANSI SQL 的扩展。这个函数的 STDDEV() 格式是为了 Oracle 兼容而提供的。

BIT_OR(expr)

返回 expr 中所有比特位的位 OR。计算以 64 位 (BIGINT) 精度执行。

BIT_AND(expr)

返回 expr 中所有比特位的位 AND。计算以 64 位 (BIGINT) 精度执行。

示例(译者注):

mysql> CREATE TABLE `ta` (

    ->   `id` smallint(5) unsigned NOT NULL default ‘0’,

    ->   KEY `id` (`id`)

    -> ) TYPE=MyISAM;

Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO `ta` VALUES(“1”),(“2”),(“3”),(“4”);

Query OK, 8 rows affected (0.00 sec)

Records: 4  Duplicates: 0  Warnings: 0

mysql> SELECT BIT_OR(id) from ta;

+————+

| BIT_OR(id) |

+————+

|          7 |

+————+

1 row in set (0.00 sec)

#     ..0001

#     ..0010

#     ..0011

#     ..0100

#  OR ..0000

#  ———

#     ..0111

mysql> SELECT BIT_AND(id) from ta;

+————-+

| BIT_AND(id) |

+————-+

|           0 |

+————-+

1 row in set (0.00 sec)

#      ..0001

#      ..0010

#      ..0011

#      ..0100

#  AND ..1111

#  ———-

#      ..0000

* 以上结果在 MySQL 4.1 中测试

示例结束(译者注)

MySQL 扩展了 GROUP BY 的用法。在 SELECT 表达式中,你可以使用或计算没有出现在 GROUP BY 部分中的列。这代表 这个组的任何可能的值。你可以使用它避免在不必要的分类项目上进行排序和分组,这样会得到更好的性能。举例来说,在下面的例子中,你不必要以 customer.name 进行分组:

mysql> SELECT order.custid,customer.name,MAX(payments)

    ->        FROM order,customer

    ->        WHERE order.custid = customer.custid

    ->        GROUP BY order.custid;

在 ANSI SQL 中,必须将customer.name 添加到 GROUP BY 子句。而在 MySQL 中,如果没有以 ANSI 模式运行,该名是多余的。

如果你在 GROUP BY 部分省略的列在分组中不是唯一的,请不要使用这个特征!否则将得到不可预知的结果。

在某些情况下,可以使用 MIN() 和 MAX() 获得一个特定的列值,即使它不是唯一的。下面的示例取出包含了 sort 列中最小值的记录行中的 column 的值:

SUBSTR(MIN(CONCAT(RPAD(sort,6,’ ‘),column)),7)

查看章节 3.5.4 拥有某个字段的组间最大值的记录行。

注意,如果你所使用的是 MySQL 3.22 (或更早的版本),或者你正试图遵从 ANSI SQL,你不能在 GROUP BY 或 ORDER BY 子句中使用表达式。你可以使用表达式的别名来应付这个限制:

mysql> SELECT id,FLOOR(value/100) AS val FROM tbl_name

    ->        GROUP BY id,val ORDER BY val;

在 MySQL 3.23 中,你可以这样做:

mysql> SELECT id,FLOOR(value/100) FROM tbl_name ORDER BY RAND();

MySQL,sqlplus的基本命令

引用

                                                                                  MySQL的基本命令

  [ ]中的内容为可选项

  –创建数据库

  mysql> create database 数据库名称

  –创建表

  mysql> create table 表名 (

  列的名字(id)类型(int(4))primary key(定义主键) auto_increment(描述 自增),

  ……,

  );

  –查看所有数据库

  mysql> show databases ;

  –使用某个数据库

  mysql> use 数据库名称;

  –查看所使用数据库下所有的表

  mysql> show tables;

  –显示表的属性结构

  mysql> desc 表名;

  –选择表中数据的显示

  –* 代表选择所有列 ,

  mysql> select * from 表名 where id= [and name=?] [or name= ];

  mysql> select id,name from 表名order by 某一列的名称 desc(降序,asc为升序)

  –删除表中的数据

  mysql> delete from table where id=?[or name=  (and name= )];

  –删除表

  mysql> drop table;

  –删除数据库

  mysql> drop database;

  索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。

  注:[1]索引不是万能的!索引可以加快数据检索操作,但会使数据修改操作变慢。每修改数据记 录,索引就必须刷新一次。为了在某种程序上弥补这一缺陷,许多SQL命令都有一个DELAY_KEY_WRITE项。这个选项的作用是暂时制止 MySQL在该命令每插入一条新记录和每修改一条现有之后立刻对索引进行刷新,对索引的刷新将等到全部记录插入/修改完毕之后再进行。在需要把许多新记录 插入某个数据表的场合,DELAY_KEY_WRITE选项的作用将非常明显。[2]另外,索引还会在硬盘上占用相当大的空间。因此应该只为最经常查询和 最经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。

  从理论上讲,完全可以为数据表里的每个字段分别建一个索引,但MySQL把同一个数据表里的索引总数限制为16个。

  1. InnoDB数据表的索引

  与MyISAM数据表相比,在 InnoDB数据表上,索引对InnoDB数据表的重要性要在得多。在InnoDB数据表上,索引不仅会在搜索数据记录时发挥作用,还是数据行级锁定机制 的苊、基础。”数据行级锁定”的意思是指在事务操作的执行过程中锁定正在被处理的个别记录,不让其他用户进行访问。这种锁定将影响到(但不限 于)SELECT…LOCK IN SHARE MODE、SELECT…FOR UPDATE命令以及INSERT、UPDATE和DELETE命令。

  出于效率方面的考虑,InnoDB数据表的数据行级锁定实际发生在它们的索引上,而不是数据表自身上。显然,数据行级锁定机制只有在有关的数据表有一个合适的索引可供锁定的时候才能发挥效力。

  2. 限制

  如果WHERE子句的查询条件里有不等号(WHERE coloum != …),MySQL将无法使用索引。

  类似地,如果WHERE子句的查询条件里使用了函数(WHERE DAY(column) = …),MySQL也将无法使用索引。

  在JOIN操作中(需要从多个数据表提取数据时),MySQL只有在主键和外键的数据类型相同时才能使用索引。

  如果WHERE子句的查询条件里使用比较操作符LIKE和REGEXP,MySQL 只有在搜索模板的第一个字符不是通配符的情况下才能使用索引。比如说,如果查询条件是LIKE ‘abc%’,MySQL将使用索引;如果查询条件是LIKE ‘%abc’,MySQL将不使用索引。

  在ORDER BY操作中,MySQL只有在排序条件不是一个查询条件表达式的情况下才使用索引。(虽然如此,在涉及多个数据表查询里,即使有索引可用,那些索引在加快ORDER BY方面也没什么作用)

  如果某个数据列里包含许多重复的值,就算为它建立了索引也不会有很好的效果。比如说,如果某个数据列里包含的净是些诸如”0/1″或”Y/N”等值,就没有必要为它创建一个索引。



安装MySQL时候的注意事项


  1、如果您是用MySQL+Apache,使用的又是FreeBSD网路操作系统的话,安装时候你应按注意到FreeBSD的版本问题,在FreeBSD的3.0以下版本来说。[3]MySQL Source内含的MIT-pthread运行是正常的,但在这版本以上,你必须使用native threads,也就是加入一个with-named-thread-libs=-lc_r的选项。

  2、如果您在COMPILE过程中出了问题,请先检查你的gcc版本是否在2.81版本以上,gmake版本是否在3.75以上。

  3、如果不是版本的问题,那可能是你的内存不足,请使用./configure — with-low-memory来加入。

  4、如果您要重新做你的configure,那么你可以键入rm config.cache和make clean来清除记录。

  5、我们一般把MySQL安装在/usr/local目录下,这是缺省值,您也可以按照你的需要设定你所安装的目录。

初学MySQL哪些需要你知道

一、连接MYSQL

  格式: mysql -h主机地址 -u用户名 -p用户密码

  1、例1:连接到本机上的MYSQL。

  首先在打开DOS窗口,然后进入目录 mysqlbin,再键入命令mysql -uroot -p,回车后提示你输密码,如果刚安装好MYSQL,超级用户root是没有密码的,故直接回车即可进入到MYSQL中了,MYSQL的提示符是:mysql>

  2、例2:连接到远程主机上的MYSQL。假设远程主机的IP为:110.110.110.110,用户名为root,密码为abcd123。则键入以下命令:

  mysql -h110.110.110.110 -uroot -pabcd123

  (注:u与root可以不用加空格,其它也一样)

  3、退出MYSQL命令: exit (回车)

  注意:想要成功连接到远程主机,需要在远程主机打开MySQL远程访问权限

  方法如下:

  在远程主机中以管理员伙身份进入

  输入如下命令

  mysql>GRANT ALL PRIVILEGES ON *.* TO ‘agui’@%’IDENTIFIED BY ‘123’ WITH GRANT OPTION;

  FLUSH PRIVILEGES;

  //赋予任何主机访问数据的权限

  mysql>FLUSH PRIVILEGES

  //修改生效

  agui为我们使用的用户名

  密码为123

  即:在远程主机上作好设置,我们即可通过mysql -h110.110.110.110 -uagui -p123连接进远程主机

二、修改密码

  格式:mysqladmin -u用户名 -p旧密码 password 新密码

  1、例1:给root加个密码ab12。首先在DOS下进入目录mysqlbin,然后键入以下命令

  mysqladmin -uroot -password ab12

  注:因为开始时root没有密码,所以-p旧密码一项就可以省略了。

  2、例2:再将root的密码改为djg345。

  mysqladmin -uroot -pab12 password djg345

三、增加新用户

  (注意:和上面不同,下面的因为是MYSQL环境中的命令,所以后面都带一个分号作为命令结束符)

  格式:grant select on 数据库.* to 用户名@登录主机 identified by “密码”

  例1、增加一个用户test1密码为abc,让他可以在任何主机上登录,并对所有数据库有查询、插入、修改、删除的权限。首先用以root用户连入MYSQL,然后键入以下命令:

  grant select,insert,update,delete on *.* to test1@“%” Identified by “abc”;

  但例1增加的用户是十分危险的,你想如某个人知道test1的密码,那么他就可以在internet上的任何一台电脑上登录你的mysql数据库并对你的数据可以为所欲为了,解决办法见例2。

  例2、增加一个用户test2密码为abc,让他只可以在localhost上登录,并可以对 数据库mydb进行查询、插入、修改、删除的操作(localhost指本地主机,即MYSQL数据库所在的那台主机),这样用户即使用知道test2的 密码,他也无法从internet上直接访问数据库,只能通过MYSQL主机上的web页来访问了。

  grant select,insert,update,delete on mydb.* to test2@localhost identified by “abc”;

  如果你不想test2有密码,可以再打一个命令将密码消掉。

  grant select,insert,update,delete on mydb.* to test2@localhost identified by “”;

  下面来看看MYSQL中有关数据库方面的操作。注意:必须首先登录到MYSQL中,以下操作都是在MYSQL的提示符下进行的,而且每个命令以分号结束。

四、操作技巧

  1、如果你打命令时,回车后发现忘记加分号,你无须重打一遍命令,只要打个分号回车就可以了。也就是说你可以把一个完整的命令分成几行来打,完后用分号作结束标志就OK。

  2、你可以使用光标上下键调出以前的命令。但以前我用过的一个MYSQL旧版本不支持。我现在用的是mysql-3.23.27-beta-win。

五、显示命令

  1、显示数据库列表。

  show databases;

  刚开始时才两个数据库:mysql和test。mysql库很重要它里面有MYSQL的系统信息,我们改密码和新增用户,实际上就是用这个库进行操作。

  2、显示库中的数据表:

  use mysql; //打开库,学过FOXBASE的一定不会陌生吧

  show tables;

  3、显示数据表的结构:

  describe 表名;

  4、建库:

  create database 库名;

  5、建表:

  use 库名;

  create table 表名 (字段设定列表);

  6、删库和删表:

  drop database 库名;

  drop table 表名;

  7、将表中记录清空:

  delete from 表名;

  8、显示表中的记录:

  select * from 表名;

六、一个建库和建表以及插入数据的实例

  drop database if exists school; //如果存在SCHOOL则删除

  create database school; //建立库SCHOOL

  use school; //打开库SCHOOL

  create table teacher //建立表TEACHER

  (

  id int(3) auto_increment not null primary key,

  name char(10) not null,

  address varchar(50) default ‘深圳’,

  year date

  ); //建表结束

  //以下为插入字段

  insert into teacher values(”,’glchengang’,’深圳一中‘,’1976-10-10′);

  insert into teacher values(”,’jack’,’深圳一中‘,’1975-12-23′);

  注:在建表中:

  (1)将ID设为长度为3的数字字段:int(3)并让它每个记录自动加一:auto_increment并不能为空:not null而且让他成为主字段primary key。

  (2)将NAME设为长度为10的字符字段。

  (3)将ADDRESS设为长度50的字符字段,而且缺省值为深圳。varchar和char有什么区别呢,只有等以后的文章再说了。

  (4)将YEAR设为日期字段。

  如果你在mysql提示符键入上面的命令也可以,但不方便调试。你可以将以上命令原样写入一个文本文件中假设为school.sql,然后复制到c:\下,并在DOS状态进入目录\mysql\bin,然后键入以下命令:

  mysql -uroot -p密码 < c:\school.sql

  如果成功,空出一行无任何显示;如有错误,会有提示。(以上命令已经调试,你只要将//的注释去掉即可使用)。

七、将文本数据转到数据库中

  1、文本数据应符合的格式:字段数据之间用tab键隔开,null值用\n来代替.

  例:

  3 rose 深圳二中 1976-10-10

  4 mike 深圳一中 1975-12-23

  2、数据传入命令 load data local infile “文件名” into table 表名;

  注意:你最好将文件复制到\mysql\bin目录下,并且要先用use命令打开表所在的数据库 。

  八、备份数据库:(命令在DOS的\mysql\bin目录下执行)

  mysqldump –opt school>school.bbb

  注释:将数据库school备份到school.bbb文件,school.bbb是一个文本文件,文件名任取,打开看看你会有新发现。[4]

MySQL安全性指南

  作为一个MySQL的系统管理员,你有责任维护你的MySQL数据库系统的数据安全性[5]和完整性。下面主要主要介绍如何建立一个安全的MySQL系统,从系统内部和外部网络两个角度,提供一个安全指南。

  一、内部安全性-保证数据目录访问的安全

  1.1 数据库文件。

  1.2 日志文件。

  二、外部安全性-保证网络访问的安全

  2.1 MySQL授权表的结构和内容

  2.2 服务器控制客户访问

  2.3 避免授权表风险

2.不用GRANT设置用户

MySQL是一个小型关系型数据库管理系统,开发者为瑞典MySQLAB公司,在2008年1月16号被Sun公司收购。MySQL被广泛地应用在 Internet上的中小型网站中。由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,许多中小型网站为了降低网站总体拥有成本而选择了 MySQL作为网站数据库。

MySQL的版本构架介绍

单点(Single),适合小规模应用

复制(Replication),适合中小规模应用

集群(Cluster),适合大规模应用

MySQL公司在同时开发两个版本的软件,4.1版以及5.0版。4.1版本的代码已经发布并有望在8个月后公布最终代码。而5.0版本的最后产品将在6个月后发布。

MySQL4.1版本中增加了不少新的性能,包括对主键的更高速度的缓存,对子查询的更好的支持,以及应网络约会网站所要求的,基于地理信息的查询。

其同步开发的5.0版本则把目标对准了企业用户,对于4.1版本中的所有新特性,5.0版本悉数收入囊中,并且独具以下特点:对外键的良好支持;系统自动报错机制以及对存储过程的充分支持。

MySQL的索引文件介绍

索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。索引不是万能的,索引可以 加快数据检索操作,但会使数据修改操作变慢。每修改数据记录,索引就必须刷新一次。为了在某种程序上弥补这一缺陷,许多SQL命令都有一个 DELAY_KEY_WRITE项。这个选项的作用是暂时制止MySQL在该命令每插入一条新记录和每修改一条现有之后立刻对索引进行刷新,对索引的刷新 将等到全部记录插入/修改完毕之后再进行。在需要把许多新记录插入某个数据表的场合,DELAY_KEY_WRITE选项的作用将非常明显。另外,索引还 会在硬盘上占用相当大的空间。因此应该只为最经常查询和最经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的 实际效果。

从理论上讲,完全可以为数据表里的每个字段分别建一个索引,但MySQL把同一个数据表里的索引总数限制为16个。

1、InnoDB数据表的索引

与InnoDB数据表相比,在InnoDB数据表上,索引对InnoDB数据表的重要性要在得多。在InnoDB数据表上,索引不仅会在搜索数据记 录时发挥作用,还是数据行级锁定机制的苊、基础。“数据行级锁定”的意思是指在事务操作的执行过程中锁定正在被处理的个别记录,不让其他用户进行访问。这 种锁定将影响到(但不限于)SELECT、LOCKINSHAREMODE、SELECT、FORUPDATE命令以及INSERT、UPDATE和 DELETE命令。出于效率方面的考虑,InnoDB数据表的数据行级锁定实际发生在它们的索引上,而不是数据表自身上。显然,数据行级锁定机制只有在有 关的数据表有一个合适的索引可供锁定的时候才能发挥效力。

2、限制

如果WEHERE子句的查询条件里有不等号(WHEREcoloum!=),MySQL将无法使用索引。类似地,如果WHERE子句的查询条件里使 用了函数(WHEREDAY(column)=),MySQL也将无法使用索引。在JOIN操作中(需要从多个数据表提取数据时),MySQL只有在主键 和外键的数据类型相同时才能使用索引。

如果WHERE子句的查询条件里使用比较操作符LIKE和REGEXP,MySQL只有在搜索模板的第一个字符不是通配符的情况下才能使用索引。比 如说,如果查询条件是LIKE’abc%‘,MySQL将使用索引;如果查询条件是LIKE’%abc’,MySQL将不使用索引。

在ORDERBY操作中,MySQL只有在排序条件不是一个查询条件表达式的情况下才使用索引。(虽然如此,在涉及多个数据表查询里,即使有索引可 用,那些索引在加快ORDERBY方面也没什么作用)。如果某个数据列里包含许多重复的值,就算为它建立了索引也不会有很好的效果。比如说,如果某个数据 列里包含的净是些诸如“0/1”或“Y/N”等值,就没有必要为它创建一个索引。

MySQL 普通索引、唯一索引和主索引

1、普通索引

普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度。因此,应该只为那些最经常出现在查询条件 (WHEREcolumn=)或排序条件(ORDERBYcolumn)中的数据列创建索引。只要有可能,就应该选择一个数据最整齐、最紧凑的数据列(如 一个整数类型的数据列)来创建索引。

2、唯一索引

普通索引允许被索引的数据列包含重复的值。比如说,因为人有可能同名,所以同一个姓名在同一个“员工个人资料”数据表里可能出现两次或更多次。

如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该用关键字UNIQUE把它定义为一个唯一索引。这么做的好处: 一是简化了MySQL对这个索引的管理工作,这个索引也因此而变得更有效率;二是MySQL会在有新记录插入数据表时,自动检查新记录的这个字段的值是否 已经在某个记录的这个字段里出现过了;如果是,MySQL将拒绝插入那条新记录。也就是说,唯一索引可以保证数据记录的唯一性。事实上,在许多场合,人们 创建唯一索引的目的往往不是为了提高访问速度,而只是为了避免数据出现重复。

3、主索引

在前面已经反复多次强调过:必须为主键字段创建一个索引,这个索引就是所谓的“主索引”。主索引与唯一索引的唯一区别是:前者在定义时使用的关键字是PRIMARY而不是UNIQUE。

4、外键索引

如果为某个外键字段定义了一个外键约束条件,MySQL就会定义一个内部索引来帮助自己以最有效率的方式去管理和使用外键约束条件。

5、复合索引

索引可以覆盖多个数据列,如像INDEX(columnA,columnB)索引。这种索引的特点是MySQL可以有选择地使用一个这样的索引。如 果查询操作只需要用到columnA数据列上的一个索引,就可以使用复合索引INDEX(columnA,columnB)。不过,这种用法仅适用于在复 合索引中排列在前的数据列组合。比如说,INDEX(A,B,C)可以当做A或(A,B)的索引来使用,但不能当做B、C或(B,C)的索引来使用。

6、索引的长度

在为CHAR和VARCHAR类型的数据列定义索引时,可以把索引的长度限制为一个给定的字符个数(这个数字必须小于这个字段所允许的最大字符个 数)。这么做的好处是可以生成一个尺寸比较小、检索速度却比较快的索引文件。在绝大多数应用里,数据库中的字符串数据大都以各种各样的名字为主,把索引的 长度设置为10~15个字符已经足以把搜索范围缩小到很少的几条数据记录了。在为BLOB和TEXT类型的数据列创建索引时,必须对索引的长度做出限 制;MySQL所允许的最大索引全文索引文本字段上的普通索引只能加快对出现在字段内容最前面的字符串(也就是字段内容开头的字符)进行检索操作。如果字 段里存放的是由几个、甚至是多个单词构成的较大段文字,普通索引就没什么作用了。这种检索往往以的形式出现,这对MySQL来说很复杂,如果需要处理的数 据量很大,响应时间就会很长。

这类场合正是全文索引(full-textindex)可以大显身手的地方。在生成这种类型的索引时,MySQL将把在文本中出现的所有单词创建为 一份清单,查询操作将根据这份清单去检索有关的数据记录。全文索引即可以随数据表一同创建,也可以等日后有必要时再使用下面这条命令添 加:ALTERTABLEtablenameADDFULLTEXT(column1,column2)有了全文索引,就可以用SELECT查询命令去检 索那些包含着一个或多个给定单词的数据记录了。下面是这类查询命令的基本语法:

SELECT*FROMtablename

WHEREMATCH(column1,column2)AGAINST(‘word1′,’word2′,’word3’)

上面这条命令将把column1和column2字段里有word1、word2和word3的数据记录全部查询出来。

注解:InnoDB数据表不支持全文索引。

MySQL文件优化

查询和索引的优化

只有当数据库里已经有了足够多的测试数据时,它的性能测试结果才有实际参考价值。如果在测试数据库里只有几百条数据记录,它们往往在执行完第一条查 询命令之后就被全部加载到内存里,这将使后续的查询命令都执行得非常快–不管有没有使用索引。只有当数据库里的记录超过了1000条、数据总量也超过了 MySQL服务器上的内存总量时,数据库的性能测试结果才有意义。

在不确定应该在哪些数据列上创建索引的时候,人们从EXPLAINSELECT命令那里往往可以获得一些帮助。这其实只是简单地给一条普通的 SELECT命令加一个EXPLAIN关键字作为前缀而已。有了这个关键字,MySQL将不是去执行那条SELECT命令,而是去对它进行分析。 MySQL将以表格的形式把查询的执行过程和用到的索引等信息列出来。

在EXPLAIN命令的输出结果里,第1列是从数据库读取的数据表的名字,它们按被读取的先后顺序排列。type列指定了本数据表与其它数据表之间 的关联关系(JOIN)。在各种类型的关联关系当中,效率最高的是system,然后依次是const、eq_ref、ref、range、index和 All(All的意思是:对应于上一级数据表里的每一条记录,这个数据表里的所有记录都必须被读取一遍——这种情况往往可以用一索引来避免)。

possible_keys数据列给出了MySQL在搜索数据记录时可选用的各个索引。key数据列是MySQL实际选用的索引,这个索引按字节计 算的长度在key_len数据列里给出。比如说,对于一个INTEGER数据列的索引,这个字节长度将是4。如果用到了复合索引,在key_len数据列 里还可以看到MySQL具体使用了它的哪些部分。作为一般规律,key_len数据列里的值越小越好。

ref数据列给出了关联关系中另一个数据表里的数据列的名字。row数据列是MySQL在执行这个查询时预计会从这个数据表里读出的数据行的个数。row数据列里的所有数字的乘积可以大致了解这个查询需要处理多少组合。

最后,extra数据列提供了与JOIN操作有关的更多信息,比如说,如果MySQL在执行这个查询时必须创建一个临时数据表,就会在extra列看到usingtemporary字样。

MySQL5解压缩版windows下安装配置应用总结_jackey

说明:本文针对mysql-noinstall版本,也就是解压缩版的安装配置应用做了个总结,这些操作都是平时很常用的操作。文章中不对mysql的可执行文件安装版做介绍了,可执行安装版有很多的弊端,我也不一一说了。总之,我喜欢绿色环保的,包括eclipse、tomcat、jboss、apache也是,即使操作系统重装了,这些软件也不需要重装,可谓一劳永逸!
 
环境:
Windows 2000/XP/2003
mysql-noinstall-5.0.37-win32.zip
 
一、下载MySQL
 
二、安装过程
 
1、解压缩mysql-noinstall-5.0.37-win32.zip到一个目录,加入解压缩到E:\myserver目录。
 
2、编写mysql的运行配置文件my.ini
my.ini
—————————–
[WinMySQLAdmin]
# 指定mysql服务启动启动的文件
Server=E:\\myserver\\mysql-5.0.37-win32\\bin\\mysqld-nt.exe
 
[mysqld]
# 设置mysql的安装目录
basedir=E:\\myserver\\mysql-5.0.37-win32
# 设置mysql数据库的数据的存放目录,必须是data,或者是
\\xxx\data
datadir=E:\\myserver\\mysql-5.0.37-win32\\data
# 设置mysql服务器的字符集
default-character-set=gbk
 
[client]
# 设置mysql客户端的字符集
default-character-set=gbk
—————————–
 
如果你不想手写my.ini,也可以直接修改MySQL下面自带的ini文件。
修改D:\mysql-5.0.37-win32\my-small.ini文件内容,添加红色内容

     
[client]
#password = your_password
port  = 3306
socket  = /tmp/mysql.sock
default-character-set=gbk
 
[mysqld]
port  = 3306
socket  = /tmp/mysql.sock
default-character-set=gbk
skip-locking
key_buffer = 16K
max_allowed_packet = 1M
table_cache = 4
sort_buffer_size = 64K
read_buffer_size = 256K
read_rnd_buffer_size = 256K
net_buffer_length = 2K
thread_stack = 64K
 
修改完成后保存即可。
 
3、安装mysql服务
从MS-DOS窗口进入目录E:\myserver\mysql-5.0.37-win32\bin,运行如下命令:
mysqld –install mysql5 –defaults-file=E:\myserver\mysql-5.0.37-win32\my.ini
 
4、启动mysql数据库
还在上面的命令窗口里面,输入命令:net start mysql5
这样就启动了mysql服务。
 
5、(本地)登录mysql数据库
还在上面的命令窗口里面,输入命令:mysql -u root -p
回车后提示输入密码。
mysql解压缩版初次安装管理员root的密码为空,因此直接再回车一次就登入mysql数据库了。
 
如果你不是初次登录mysql,你还拥有网络地址的用户,那么你可以用如下命令登录到mysql服务器,这个mysql服务器也许在远方,也许在本地。这种登录方式叫“远程登录”,命令如下:
mysql -h 192.168.3.143 -u root -p
mysql -h 192.168.3.143 -u root -pleizhimin
 
-h是指定登录ip,-u指定用户,-p指定密码,-p后如果什么都不写,那么接下来会提示输入密码,-p后也可以直接写上密码,这样就不再需要输入密码了。
 
6、操作数据库和表
登录mysql数据库后,就可以执行指定操作数据库,用命令:use 数据库名
指定了操作的数据库对象后,就可以操作数据库中的表了,操作方法当然是SQL命令了,呵呵。
 
7、更改mysql数据库管理员root的密码
mysql数据库中默认有个mysql数据库,这个是mysql系统的数据库,用来保存数据库用户、权限等等很多信息。要更改密码,就要操作mysql数据库的user表。
现在mysql的root用户密码还为空,很不安全的,假设要更改密码为“leizhimin”。
 
还在上面的命令窗口里面,执行如下命令:
use mysql;
grant all on *.* to root@’%’ identified by ‘leizhimin’ with grant option;
commit;
这段命令的含义是,添加一个root用户,拥有所有的权限,密码为“leizhimin”,并且这个用户不但可以本地访问,也可以通过网络访问。强调这个原因是mysql系统自带的的那个root用户只能从本地访问,它@字符后面的标识是localhost。具体可以查看mysql数据的uer表看看,这样以来,就有两个root用户了,一个是系统原来的,一个新建的,为了管理的方便,就将mysql自带root删除,保留刚创建的这个root用户,原因是这个用户可以通过网络访问mysql。
 
然后,删除用户的命令:
use mysql
delete from user where user=’root’ and host=’localhost’;
commit;
 
其实上面的方法是授权命令,在授权的同时创建了数据库用户。mysql也有单独的修改用户密码的方法,下面看看如何操作。
首先,先建立一个用户lavasoft,密码为:123456
grant all on *.* to lavasoft@’localhost’ identified by ‘123456’ with grant  option;
 
接下来就修改这个用户的密码为:leizhimin
update user set password = password(‘leizhimin’) where user = ‘lavasoft’ and host=’localhost’;
flush privileges;
 
说明一点,最好用grant的方式创建mysql用户,尤其对mysql DBA来说,创建用户的同时要指定用户权限,养成好习惯很重要的。
 
这个修改方法实际上用的是mysql函数来进行的,还有更多的方法,我就不一一介绍了。
还要注意一点就是在修改密码等操作的时候,mysql不允许为表指定别名,但是初次在外却没有这个限制。

8、创建数据库
实际上mysql数据库中除了mysql数据库外,还有一个空的数据库test,供用户测试使用。
现在继续创建一个数据库testdb,并执行一系列sql语句看看mysql数据库的基本操作。
 
创建数据库testdb:
create database testdb;
 
预防性创建数据库:
create database if not testdb
 
创建表:
use testdb;
create table table1(
username varchar(12),
password varchar(20));
 
预防性创建表aaa:
create table if not exists aaa(ss varchar(20));
 
查看表结构:
describe table1;
 
插入数据到表table1:
insert into table1(username,password) values
(‘leizhimin’,’lavasoft’),
(‘hellokitty’,’hahhahah’);
commit;
 
查询表table1:
select * from table1;
 
更改数据:
update table1 set password=’hehe’ where username=’hellokitty’;
commit;
 
删除数据:
delete from  table1 where username=’hellokitty’;
commit;
 
给表添加一列:
alter table table1 add column(
  sex varchar(2) comment ‘性别’,
  age date not null comment ‘年龄’
);
commit;
 
从查询创建一个表table1
create table tmp as
select * from table1;
 
删除表table1:
drop table if exists table1;
drop table if exists tmp;
 
9、备份数据库testdb
mysqldump -h 192.168.3.143 -u root -pleizhimin -x –default-character-set=gbk >C:\testdb.sql
 
10、删除数据库testdb
drop database testdb;
 
11、恢复testdb数据库
首先先建立testdb数据库,然后用下面命令进行本地恢复:
mysql -u root -pleizhimin testdb <C:\testdb.sql
 
12、删除mysql服务
假如你厌倦mysql了,你需要卸载,那么你只需要这么做
 
停止mysql服务
net stop mysql5
 
删除mysql服务
sc delete mysql5

然后删除msyql的安装文件夹,不留任何痕迹。
 
呵呵,现在看来还是oninstall(非安装解压缩)版的mysql好,绿色环保。
好了,不说了,相信你已经掌握mysql的基本操作了。
 
看懂了这个,mysql的exe版还有什么好玩的,嘿嘿。。。
 
参考资料:
mysql中自带的文档!