Python学习-基础篇9 mysql相关
mysql一:初始数据库
一 数据库管理软件的由来
基于我们之前所学,数据要想永久保存,都是保存于文件中,毫无疑问,一个文件仅仅只能存在于某一台机器上。
如果我们暂且忽略直接基于文件来存取数据的效率问题,并且假设程序所有的组件都运行在一台机器上,那么用文件存取数据,并没有问题。
很不幸,这些假设都是你自己意淫出来的,上述假设存在以下几个问题。。。。。。
1、程序所有的组件就不可能运行在一台机器上
#因为这台机器一旦挂掉则意味着整个软件的崩溃,并且程序的执行效率依赖于承载它的硬件,而一台机器机器的性能总归是有限的,受限于目前的硬件水平,就一台机器的性能垂直进行扩展是有极限的。
#于是我们只能通过水平扩展来增强我们系统的整体性能,这就需要我们将程序的各个组件分布于多台机器去执行。
2、数据安全问题
#根据1的描述,我们将程序的各个组件分布到各台机器,但需知各组件仍然是一个整体,言外之意,所有组件的数据还是要共享的。但每台机器上的组件都只能操作本机的文件,这就导致了数据必然不一致。 #于是我们想到了将数据与应用程序分离:把文件存放于一台机器,然后将多台机器通过网络去访问这台机器上的文件(用socket实现),即共享这台机器上的文件,共享则意味着竞争,会发生数据不安全,需要加锁处理。。。。
3、并发
根据2的描述,我们必须写一个socket服务端来管理这台机器(数据库服务器)上的文件,然后写一个socket客户端,完成如下功能:
#1.远程连接(支持并发) #2.打开文件 #3.读写(加锁) #4.关闭文件
总结:
#我们在编写任何程序之前,都需要事先写好基于网络操作一台主机上文件的程序(socket服务端与客户端程序),于是有人将此类程序写成一个专门的处理软件,这就是mysql等数据库管理软件的由来,但mysql解决的不仅仅是数据共享的问题,还有查询效率,安全性等一系列问题,总之,把程序员从数据管理中解脱出来,专注于自己的程序逻辑的编写。
二 数据库概述
1 什么是数据(Data)
描述事物的符号记录称为数据,描述事物的符号既可以是数字,也可以是文字、图片,图像、声音、语言等,数据由多种表现形式,它们都可以经过数字化后存入计算机
在计算机中描述一个事物,就需要抽取这一事物的典型特征,组成一条记录,就相当于文件里的一行内容,如:
1 egon,male,18,1999,山东,计算机系,2017,oldboy
单纯的一条记录并没有任何意义,如果我们按逗号作为分隔,依次定义各个字段的意思,相当于定义表的标题
1 name,sex,age,birth,born_addr,major,entrance_time,school #字段 2 egon,male,18,1999,山东,计算机系,2017,oldboy #记录
这样我们就可以了解egon,性别为男,年龄18岁,出生于1999年,出生地为山东,2017年考入老男孩计算机系
2 什么是数据库(DataBase,简称DB)
数据库即存放数据的仓库,只不过这个仓库是在计算机存储设备上,而且数据是按一定的格式存放的
过去人们将数据存放在文件柜里,现在数据量庞大,已经不再适用
数据库是长期存放在计算机内、有组织、可共享的数据即可。
数据库中的数据按一定的数据模型组织、描述和储存,具有较小的冗余度、较高的数据独立性和易扩展性,并可为各种 用户共享
3 什么是数据库管理系统(DataBase Management System 简称DBMS)
在了解了Data与DB的概念后,如何科学地组织和存储数据,如何高效获取和维护数据成了关键
这就用到了一个系统软件---数据库管理系统
如MySQL、Oracle、SQLite、Access、MS SQL Server
mysql主要用于大型门户,例如搜狗、新浪等,它主要的优势就是开放源代码,因为开放源代码这个数据库是免费的,他现在是甲骨文公司的产品。
oracle主要用于银行、铁路、飞机场等。该数据库功能强大,软件费用高。也是甲骨文公司的产品。
sql server是微软公司的产品,主要应用于大中型企业,如联想、方正等。
4 数据库服务器、数据管理系统、数据库、表与记录的关系(重点理解!!!)
记录:1 刘海龙 324245234 22(多个字段的信息组成一条记录,即文件中的一行内容)
表:student,scholl,class_list(即文件)
数据库:oldboy_stu(即文件夹)
数据库管理系统:如mysql(是一个软件)
数据库服务器:一台计算机(对内存要求比较高)
总结:
数据库服务器-:运行数据库管理软件
数据库管理软件:管理-数据库
数据库:即文件夹,用来组织文件/表
表:即文件,用来存放多行内容/多条记录
5 数据库管理技术的发展历程(了解)
一 人工管理阶段
20世纪50年代中期以前,计算机主要用于科学计算。
当时的硬件水平:外存只有纸带、卡片、磁带,没有磁盘等直接存取的存储设备
当时的软件状况:没有操作系统,没有管理数据的软件,数据的处理方式是批处理。
人工管理数据具有以下特点:
1 数据不保存:计算机主要用于科学计算,数据临时用,临时输入,不保存
2 应用程序管理数据:数据要有应用程序自己管理,应用程序需要处理数据的逻辑+物理结构,开发负担很重
3 数据不共享:一组数据只对应一个程序,多个程序之间涉及相同数据时,必须各自定义,造成数据大量冗余
4 数据不具有独立性:数据的逻辑结构或物理结构发生变化后,必须对应用程序做出相应的修改,开发负担进一步加大
二 文件系统阶段
20世纪50年代后期到60年代中期
硬件水平:有了磁盘、磁鼓等可直接存取的存储设备
软件水平:有了操作系统,并且操作系统中已经有了专门的数据管理软件,即文件系统;处理方式上不仅有了批处理,而且能够联机实时处理
文件系统管理数据具有以下优点:
1 数据可以长期保存:计算机大量用于数据处理,因而数据需要长期保存,进行增删改查操作
2 由文件系统管理数据:文件系统这个软件,把数据组织成相对独立的数据文件,利用按文件名,按记录进行存取。实现了记录内的结构性,但整体无结构。并且程序与数据之间由文件系统提供存取方法进行转换,是应用程序与数据之间有了一定的独立性,程序员可以不必过多考虑物理细节。
文件系统管理数据具有以下缺点:
1 数据共享性差,冗余度大:一个文件对应一个应用程序,不同应用有相同数据时,也必须建立各自的文件,不能共享相同的数据,造成数据冗余,浪费空间,且相同的数据重复存储,各自管理,容易造成数据不一致性
2 数据独立性差:一旦数据的逻辑结构改变,必须修改应用程序,修改文件结构的定义。应用程序的改变,也将引起文件的数据结构的改变。因此数据与程序之间缺乏独立性。可见,文件系统仍然是一个不具有弹性的无结构的数据集合,即文件之间是孤立的,不能反映现实世界事物之间的内存联系。
三 数据系统阶段
20世纪60年代后期以来,计算机用于管理的规模越来越大,应用越来越广泛,数据量急剧增长,同时多种应用,多种语言互相覆盖地共享数据结合要求越来越强烈
硬件水平:有了大容量磁盘,硬件架构下降
软件水平:软件价格上升(开发效率必须提升,必须将程序员从数据管理中解放出来),分布式的概念盛行。
数据库系统的特点:
1 数据结构化(如上图odboy_stu)
2 数据共享,冗余度低,易扩充
3 数据独立性高
4 数据由DBMS统一管理和控制
a:数据的安全性保护
b:数据的完整性检查
c:并发控制
d:数据库恢复
三 mysql介绍
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下公司。MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。
mysql是什么
#mysql就是一个基于socket编写的C/S架构的软件 #客户端软件 mysql自带:如mysql命令,mysqldump命令等 python模块:如pymysql
数据库管理软件分类
#分两大类: 关系型:如sqllite,db2,oracle,access,sql server,MySQL,注意:sql语句通用 非关系型:mongodb,redis,memcache #可以简单的理解为: 关系型数据库需要有表结构 非关系型数据库是key-value存储的,没有表结构
四 下载安装
Linux版本
#二进制rpm包安装 yum -y install mysql-server mysql
源码安装mysql
1.解压tar包 cd /software tar -xzvf mysql-5.6.21-linux-glibc2.5-x86_64.tar.gz mv mysql-5.6.21-linux-glibc2.5-x86_64 mysql-5.6.21 2.添加用户与组 groupadd mysql useradd -r -g mysql mysql chown -R mysql:mysql mysql-5.6.21 3.安装数据库 su mysql cd mysql-5.6.21/scripts ./mysql_install_db --user=mysql --basedir=/software/mysql-5.6.21 --datadir=/software/mysql-5.6.21/data 4.配置文件 cd /software/mysql-5.6.21/support-files cp my-default.cnf /etc/my.cnf cp mysql.server /etc/init.d/mysql vim /etc/init.d/mysql #若mysql的安装目录是/usr/local/mysql,则可省略此步 修改文件中的两个变更值 basedir=/software/mysql-5.6.21 datadir=/software/mysql-5.6.21/data 5.配置环境变量 vim /etc/profile export MYSQL_HOME="/software/mysql-5.6.21" export PATH="$PATH:$MYSQL_HOME/bin" source /etc/profile 6.添加自启动服务 chkconfig --add mysql chkconfig mysql on 7.启动mysql service mysql start 8.登录mysql及改密码与配置远程访问 mysqladmin -u root password 'your_password' #修改root用户密码 mysql -u root -p #登录mysql,需要输入密码 mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'your_password' WITH GRANT OPTION; #允许root用户远程访问 mysql>FLUSH PRIVILEGES; #刷新权限
源码安装mariadb
1. 解压 tar zxvf mariadb-5.5.31-linux-x86_64.tar.gz mv mariadb-5.5.31-linux-x86_64 /usr/local/mysql //必需这样,很多脚本或可执行程序都会直接访问这个目录 2. 权限 groupadd mysql //增加 mysql 属组 useradd -g mysql mysql //增加 mysql 用户 并归于mysql 属组 chown mysql:mysql -Rf /usr/local/mysql // 设置 mysql 目录的用户及用户组归属。 chmod +x -Rf /usr/local/mysql //赐予可执行权限 3. 拷贝配置文件 cp /usr/local/mysql/support-files/my-medium.cnf /etc/my.cnf //复制默认mysql配置 文件到/etc目录 4. 初始化 /usr/local/mysql/scripts/mysql_install_db --user=mysql //初始化数据库 cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysql //复制mysql服务程序 到系统目录 chkconfig mysql on //添加mysql 至系统服务并设置为开机启动 service mysql start //启动mysql 5. 环境变量配置 vim /etc/profile //编辑profile,将mysql的可执行路径加入系统PATH export PATH=/usr/local/mysql/bin:$PATH source /etc/profile //使PATH生效。 6. 账号密码 mysqladmin -u root password 'yourpassword' //设定root账号及密码 mysql -u root -p //使用root用户登录mysql use mysql //切换至mysql数据库。 select user,host,password from user; //查看系统权限 drop user ''@'localhost'; //删除不安全的账户 drop user root@'::1'; drop user root@127.0.0.1; select user,host,password from user; //再次查看系统权限,确保不安全的账户均被删除。 flush privileges; //刷新权限 7. 一些必要的初始配置 1)修改字符集为UTF8 vi /etc/my.cnf 在[client]下面添加 default-character-set = utf8 在[mysqld]下面添加 character_set_server = utf8 2)增加错误日志 vi /etc/my.cnf 在[mysqld]下面添加: log-error = /usr/local/mysql/log/error.log general-log-file = /usr/local/mysql/log/mysql.log 3) 设置为不区分大小写,linux下默认会区分大小写。 vi /etc/my.cnf 在[mysqld]下面添加: lower_case_table_name=1 修改完重启:#service mysql restart
Window版本
安装
#1、下载:MySQL Community Server 5.7.16 http://dev.mysql.com/downloads/mysql/ #2、解压 如果想要让MySQL安装在指定目录,那么就将解压后的文件夹移动到指定目录,如:C:\mysql-5.7.16-winx64 #3、添加环境变量 【右键计算机】--》【属性】--》【高级系统设置】--》【高级】--》【环境变量】--》【在第二个内容框中找到 变量名为Path 的一行,双击】 --> 【将MySQL的bin目录路径追加到变值值中,用 ; 分割】 #4、初始化 mysqld --initialize-insecure #5、启动MySQL服务 mysqld # 启动MySQL服务 #6、启动MySQL客户端并连接MySQL服务 mysql -u root -p # 连接MySQL服务器
将MySQL服务制作成windows服务
上一步解决了一些问题,但不够彻底,因为在执行【mysqd】启动MySQL服务器时,当前终端会被hang住,那么做一下设置即可解决此问题: 注意:--install前,必须用mysql启动命令的绝对路径 # 制作MySQL的Windows服务,在终端执行此命令: "c:\mysql-5.7.16-winx64\bin\mysqld" --install # 移除MySQL的Windows服务,在终端执行此命令: "c:\mysql-5.7.16-winx64\bin\mysqld" --remove 注册成服务之后,以后再启动和关闭MySQL服务时,仅需执行如下命令: # 启动MySQL服务 net start mysql # 关闭MySQL服务 net stop mysql
五 mysql软件基本管理
1. 启动查看
linux平台下查看
[root@egon ~]# systemctl start mariadb #启动 [root@egon ~]# systemctl enable mariadb #设置开机自启动 Created symlink from /etc/systemd/system/multi-user.target.wants/mariadb.service to /usr/lib/systemd/system/mariadb.service. [root@egon ~]# ps aux |grep mysqld |grep -v grep #查看进程,mysqld_safe为启动mysql的脚本文件,内部调用mysqld命令 mysql 3329 0.0 0.0 113252 1592 ? Ss 16:19 0:00 /bin/sh /usr/bin/mysqld_safe --basedir=/usr mysql 3488 0.0 2.3 839276 90380 ? Sl 16:19 0:00 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/var/log/mariadb/mariadb.log --pid-file=/var/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock [root@egon ~]# netstat -an |grep 3306 #查看端口 tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN [root@egon ~]# ll -d /var/lib/mysql #权限不对,启动不成功,注意user和group drwxr-xr-x 5 mysql mysql 4096 Jul 20 16:28 /var/lib/mysql
You must reset your password using ALTER USER statement before executing this statement.
安装完mysql 之后,登陆以后,不管运行任何命令,总是提示这个 mac mysql error You must reset your password using ALTER USER statement before executing this statement. 解决方法: step 1: SET PASSWORD = PASSWORD('your new password'); step 2: ALTER USER 'root'@'localhost' PASSWORD EXPIRE NEVER; step 3: flush privileges;
2. 登录,设置密码
初始状态下,管理员root,密码为空,默认只允许从本机登录localhost 设置密码 [root@egon ~]# mysqladmin -uroot password "123" 设置初始密码 由于原密码为空,因此-p可以不用 [root@egon ~]# mysqladmin -uroot -p"123" password "456" 修改mysql密码,因为已经有密码了,所以必须输入原密码才能设置新密码 命令格式: [root@egon ~]# mysql -h172.31.0.2 -uroot -p456 [root@egon ~]# mysql -uroot -p [root@egon ~]# mysql 以root用户登录本机,密码为空
3. 忘记密码
linux平台下,破解密码的两种方式
方法一:删除授权库mysql,重新初始化
[root@egon ~]# rm -rf /var/lib/mysql/mysql #所有授权信息全部丢失!!! [root@egon ~]# systemctl restart mariadb [root@egon ~]# mysql
方法二:启动时,跳过授权库
[root@egon ~]# vim /etc/my.cnf #mysql主配置文件 [mysqld] skip-grant-table [root@egon ~]# systemctl restart mariadb [root@egon ~]# mysql MariaDB [(none)]> update mysql.user set password=password("123") where user="root" and host="localhost"; MariaDB [(none)]> flush privileges; MariaDB [(none)]> \q [root@egon ~]# #打开/etc/my.cnf去掉skip-grant-table,然后重启 [root@egon ~]# systemctl restart mariadb [root@egon ~]# mysql -u root -p123 #以新密码登录
windows平台下,5.7版本mysql,破解密码的两种方式:
方法一:
#1 关闭mysql #2 在cmd中执行:mysqld --skip-grant-tables #3 在cmd中执行:mysql #4 执行如下sql: update mysql.user set authentication_string=password('') where user = 'root'; flush privileges; #5 tskill mysqld #或taskkill -f /PID 7832 #6 重新启动mysql
方法二:
#1. 关闭mysql,可以用tskill mysqld将其杀死 #2. 在解压目录下,新建mysql配置文件my.ini #3. my.ini内容,指定 [mysqld] skip-grant-tables #4.启动mysqld #5.在cmd里直接输入mysql登录,然后操作 update mysql.user set authentication_string=password('') where user='root and host='localhost'; flush privileges; #6.注释my.ini中的skip-grant-tables,然后启动myqsld,然后就可以以新密码登录了
4. 在windows下,为mysql服务指定配置文件
强调:配置文件中的注释可以有中文,但是配置项中不能出现中文
my.ini
#在mysql的解压目录下,新建my.ini,然后配置 #1. 在执行mysqld命令时,下列配置会生效,即mysql服务启动时生效 [mysqld] ;skip-grant-tables port=3306 character_set_server=utf8 default-storage-engine=innodb innodb_file_per_table=1 #解压的目录 basedir=E:\mysql-5.7.19-winx64 #data目录 datadir=E:\my_data #在mysqld --initialize时,就会将初始数据存入此处指定的目录,在初始化之后,启动mysql时,就会去这个目录里找数据 #2. 针对客户端命令的全局配置,当mysql客户端命令执行时,下列配置生效 [client] port=3306 default-character-set=utf8 user=root password=123 #3. 只针对mysql这个客户端的配置,2中的是全局配置,而此处的则是只针对mysql这个命令的局部配置 [mysql] ;port=3306 ;default-character-set=utf8 user=egon password=4573 #!!!如果没有[mysql],则用户在执行mysql命令时的配置以[client]为准
5. 统一字符编码
#1. 修改配置文件 [mysqld] default-character-set=utf8 [client] default-character-set=utf8 [mysql] default-character-set=utf8 #mysql5.5以上:修改方式有所改动 [mysqld] character-set-server=utf8 collation-server=utf8_general_ci [client] default-character-set=utf8 [mysql] default-character-set=utf8 #2. 重启服务 #3. 查看修改结果: \s show variables like '%char%'
六 初识sql语句
有了mysql这个数据库软件,就可以将程序员从对数据的管理中解脱出来,专注于对程序逻辑的编写
mysql服务端软件即mysqld帮我们管理好文件夹以及文件,前提是作为使用者的我们,需要下载mysql的客户端,或者其他模块来连接到mysqld,然后使用mysql软件规定的语法格式去提交自己命令,实现对文件夹或文件的管理。该语法即sql(Structured Query Language 即结构化查询语言)
SQL语言主要用于存取数据、查询数据、更新数据和管理关系数据库系统,SQL语言由IBM开发。SQL语言分为3种类型: #1、DDL语句 数据库定义语言: 数据库、表、视图、索引、存储过程,例如CREATE DROP ALTER #2、DML语句 数据库操纵语言: 插入数据INSERT、删除数据DELETE、更新数据UPDATE、查询数据SELECT #3、DCL语句 数据库控制语言: 例如控制用户的访问权限GRANT、REVOKE
view code:
#1. 操作文件夹 增:create database db1 charset utf8; 查:show databases; 改:alter database db1 charset latin1; 删除: drop database db1; #2. 操作文件 先切换到文件夹下:use db1 增:create table t1(id int,name char); 查:show tables 改:alter table t1 modify name char(3); alter table t1 change name name1 char(2); 删:drop table t1; #3. 操作文件中的内容/记录 增:insert into t1 values(1,'egon1'),(2,'egon2'),(3,'egon3'); 查:select * from t1; 改:update t1 set name='sb' where id=2; 删:delete from t1 where id=1; 清空表: delete from t1; #如果有自增id,新增的数据,仍然是以删除前的最后一样作为起始。 truncate table t1;数据量大,删除速度比上一条快,且直接从零开始, auto_increment 表示:自增 primary key 表示:约束(不能重复且不能为空);加速查找
mysql二:库操作
一 系统数据库
information_schema: 虚拟库,不占用磁盘空间,存储的是数据库启动后的一些参数,如用户表信息、列信息、权限信息、字符信息等
performance_schema: MySQL 5.5开始新增一个数据库:主要用于收集数据库服务器性能参数,记录处理查询请求时发生的各种事件、锁等现象
mysql: 授权库,主要存储系统用户的权限信息
test: MySQL数据库系统自动创建的测试数据库
二 创建数据库
1 语法(help create database)
CREATE DATABASE 数据库名 charset utf8;
2 数据库命名规则:
可以由字母、数字、下划线、@、#、$ 区分大小写 唯一性 不能使用关键字如 create select 不能单独使用数字 最长128位
三 数据库相关操作
1 查看数据库 show databases; show create database db1; select database(); 2 选择数据库 USE 数据库名 3 删除数据库 DROP DATABASE 数据库名; 4 修改数据库 alter database db1 charset utf8;
mysql三:表操作
一 存储引擎介绍
存储引擎即表类型,mysql根据不同的表类型会有不同的处理机制
详见:http://www.cnblogs.com/linhaifeng/articles/7213670.html
二 表介绍
表相当于文件,表中的一条记录就相当于文件的一行内容,不同的是,表中的一条记录有对应的标题,称为表的字段
id,name,qq,age称为字段,其余的,一行内容称为一条记录
三 创建表
#语法: create table 表名( 字段名1 类型[(宽度) 约束条件], 字段名2 类型[(宽度) 约束条件], 字段名3 类型[(宽度) 约束条件] ); #注意: 1. 在同一张表中,字段名是不能相同 2. 宽度和约束条件可选 3. 字段名和类型是必须的
view code:
MariaDB [(none)]> create database db1 charset utf8; MariaDB [(none)]> use db1; MariaDB [db1]> create table t1( -> id int, -> name varchar(50), -> sex enum('male','female'), -> age int(3) -> ); MariaDB [db1]> show tables; #查看db1库下所有表名 MariaDB [db1]> desc t1; +-------+-----------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-----------------------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | | name | varchar(50) | YES | | NULL | | | sex | enum('male','female') | YES | | NULL | | | age | int(3) | YES | | NULL | | +-------+-----------------------+------+-----+---------+-------+ MariaDB [db1]> select id,name,sex,age from t1; Empty set (0.00 sec) MariaDB [db1]> select * from t1; Empty set (0.00 sec) MariaDB [db1]> select id,name from t1; Empty set (0.00 sec)
往表中插入数据
MariaDB [db1]> insert into t1 values -> (1,'egon',18,'male'), -> (2,'alex',81,'female') -> ; MariaDB [db1]> select * from t1; +------+------+------+--------+ | id | name | age | sex | +------+------+------+--------+ | 1 | egon | 18 | male | | 2 | alex | 81 | female | +------+------+------+--------+ MariaDB [db1]> insert into t1(id) values -> (3), -> (4); MariaDB [db1]> select * from t1; +------+------+------+--------+ | id | name | age | sex | +------+------+------+--------+ | 1 | egon | 18 | male | | 2 | alex | 81 | female | | 3 | NULL | NULL | NULL | | 4 | NULL | NULL | NULL | +------+------+------+--------+
注意注意注意:表中的最后一个字段不要加逗号
四 查看表结构
MariaDB [db1]> describe t1; #查看表结构,可简写为desc 表名 +-------+-----------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-----------------------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | | name | varchar(50) | YES | | NULL | | | sex | enum('male','female') | YES | | NULL | | | age | int(3) | YES | | NULL | | +-------+-----------------------+------+-----+---------+-------+ MariaDB [db1]> show create table t1\G; #查看表详细结构,可加\G
五 数据类型
http://www.cnblogs.com/linhaifeng/articles/7233411.html
六 表完整性约束
http://www.cnblogs.com/linhaifeng/articles/7238814.html
七 修改表ALTER TABLE
语法: 1. 修改表名 ALTER TABLE 表名 RENAME 新表名; 2. 增加字段 ALTER TABLE 表名 ADD 字段名 数据类型 [完整性约束条件…], ADD 字段名 数据类型 [完整性约束条件…]; ALTER TABLE 表名 ADD 字段名 数据类型 [完整性约束条件…] FIRST; ALTER TABLE 表名 ADD 字段名 数据类型 [完整性约束条件…] AFTER 字段名; 3. 删除字段 ALTER TABLE 表名 DROP 字段名; 4. 修改字段 ALTER TABLE 表名 MODIFY 字段名 数据类型 [完整性约束条件…]; ALTER TABLE 表名 CHANGE 旧字段名 新字段名 旧数据类型 [完整性约束条件…]; ALTER TABLE 表名 CHANGE 旧字段名 新字段名 新数据类型 [完整性约束条件…];
示例:
示例: 1. 修改存储引擎 mysql> alter table service -> engine=innodb; 2. 添加字段 mysql> alter table student10 -> add name varchar(20) not null, -> add age int(3) not null default 22; mysql> alter table student10 -> add stu_num varchar(10) not null after name; //添加name字段之后 mysql> alter table student10 -> add sex enum('male','female') default 'male' first; //添加到最前面 3. 删除字段 mysql> alter table student10 -> drop sex; mysql> alter table service -> drop mac; 4. 修改字段类型modify mysql> alter table student10 -> modify age int(3); mysql> alter table student10 -> modify id int(11) not null primary key auto_increment; //修改为主键 5. 增加约束(针对已有的主键增加auto_increment) mysql> alter table student10 modify id int(11) not null primary key auto_increment; ERROR 1068 (42000): Multiple primary key defined mysql> alter table student10 modify id int(11) not null auto_increment; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 6. 对已经存在的表增加复合主键 mysql> alter table service2 -> add primary key(host_ip,port); 7. 增加主键 mysql> alter table student1 -> modify name varchar(10) not null primary key; 8. 增加主键和自动增长 mysql> alter table student1 -> modify id int not null primary key auto_increment; 9. 删除主键 a. 删除自增约束 mysql> alter table student10 modify id int(11) not null; b. 删除主键 mysql> alter table student10 -> drop primary key;
八 复制表
复制表结构+记录 (key不会复制: 主键、外键和索引) mysql> create table new_service select * from service; 只复制表结构 mysql> select * from service where 1=2; //条件为假,查不到任何记录 Empty set (0.00 sec) mysql> create table new1_service select * from service where 1=2; Query OK, 0 rows affected (0.00 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> create table t4 like employees;
mysql四:数据操作
一 介绍
MySQL数据操作: DML
========================================================
在MySQL管理软件中,可以通过SQL语句中的DML语言来实现数据的操作,包括
- 使用INSERT实现数据的插入
- UPDATE实现数据的更新
- 使用DELETE实现数据的删除
- 使用SELECT查询数据以及。
========================================================
本节内容包括:
插入数据
更新数据
删除数据
查询数据
二 插入数据INSERT
1. 插入完整数据(顺序插入) 语法一: INSERT INTO 表名(字段1,字段2,字段3…字段n) VALUES(值1,值2,值3…值n); 语法二: INSERT INTO 表名 VALUES (值1,值2,值3…值n); 2. 指定字段插入数据 语法: INSERT INTO 表名(字段1,字段2,字段3…) VALUES (值1,值2,值3…); 3. 插入多条记录 语法: INSERT INTO 表名 VALUES (值1,值2,值3…值n), (值1,值2,值3…值n), (值1,值2,值3…值n); 4. 插入查询结果 语法: INSERT INTO 表名(字段1,字段2,字段3…字段n) SELECT (字段1,字段2,字段3…字段n) FROM 表2 WHERE …;
三 更新数据UPDATE
语法: UPDATE 表名 SET 字段1=值1, 字段2=值2, WHERE CONDITION; 示例: UPDATE mysql.user SET password=password(‘123’) where user=’root’ and host=’localhost’;
四 删除数据DELETE
语法: DELETE FROM 表名 WHERE CONITION; 示例: DELETE FROM mysql.user WHERE password=’’; 练习: 更新MySQL root用户密码为mysql123 删除除从本地登录的root用户以外的所有用户
五 查询数据SELECT
单表查询:http://www.cnblogs.com/linhaifeng/articles/7267592.html
多表查询:http://www.cnblogs.com/linhaifeng/articles/7267596.html
六 权限管理
#授权表 user #该表放行的权限,针对:所有数据,所有库下所有表,以及表下的所有字段 db #该表放行的权限,针对:某一数据库,该数据库下的所有表,以及表下的所有字段 tables_priv #该表放行的权限。针对:某一张表,以及该表下的所有字段 columns_priv #该表放行的权限,针对:某一个字段 #按图解释: user:放行db1,db2及其包含的所有 db:放行db1,及其db1包含的所有 tables_priv:放行db1.table1,及其该表包含的所有 columns_prive:放行db1.table1.column1,只放行该字段
mysql五:索引原理与慢查询优化
一 介绍
为何要有索引?
一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,因此对查询语句的优化显然是重中之重。说起加速查询,就不得不提到索引了。
什么是索引?
索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能
非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发重要。
索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高好几个数量级。
索引相当于字典的音序表,如果要查某个字,如果不使用音序表,则需要从几百页中逐页去查。
30 10 40 5 15 35 66 1 6 11 19 21 39 55 100
你是否对索引存在误解?
索引是应用程序设计和开发的一个重要方面。若索引太多,应用程序的性能可能会受到影响。而索引太少,对查询性能又会产生影响,要找到一个平衡点,这对应用程序的性能至关重要。一些开发人员总是在事后才想起添加索引----我一直认为,这源于一种错误的开发模式。如果知道数据的使用,从一开始就应该在需要处添加索引。开发人员往往对数据库的使用停留在应用的层面,比如编写SQL语句、存储过程之类,他们甚至可能不知道索引的存在,或认为事后让相关DBA加上即可。DBA往往不够了解业务的数据流,而添加索引需要通过监控大量的SQL语句进而从中找到问题,这个步骤所需的时间肯定是远大于初始添加索引所需的时间,并且可能会遗漏一部分的索引。当然索引也并不是越多越好,我曾经遇到过这样一个问题:某台MySQL服务器iostat显示磁盘使用率一直处于100%,经过分析后发现是由于开发人员添加了太多的索引,在删除一些不必要的索引之后,磁盘使用率马上下降为20%。可见索引的添加也是非常有技术含量的。
二 索引的原理
一 索引原理
索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查字典,查火车车次,飞机航班等
本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。
数据库也是一样,但显然要复杂的多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对所有的问题呢?我们回想字典的例子,能不能把数据分成段,然后分段查询呢?最简单的如果1000条数据,1到100分成第一段,101到200分成第二段,201到300分成第三段......这样查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。但如果是1千万的记录呢,分成几段比较好?稍有算法基础的同学会想到搜索树,其平均复杂度是lgN,具有不错的查询性能。但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操作成本来考虑的。而数据库实现比较复杂,一方面数据是保存在磁盘上的,另外一方面为了提高性能,每次又可以把部分数据读入内存来计算,因为我们知道访问磁盘的成本大概是访问内存的十万倍左右,所以简单的搜索树难以满足复杂的应用场景。
二 磁盘IO与预读
前面提到了访问磁盘,那么这里先简单介绍一下磁盘IO和预读,磁盘读取数据靠的是机械运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所需要的时间,主流磁盘一般在5ms以下;旋转延迟就是我们经常听说的磁盘转速,比如一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。那么访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17 = 9ms左右,听起来还挺不错的,但要知道一台500 -MIPS(Million Instructions Per Second)的机器每秒可以执行5亿条指令,因为指令依靠的是电的性质,换句话说执行一次IO的时间可以执行约450万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难。下图是计算机硬件延迟的对比图,供大家参考:
考虑到磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。
三 索引的数据结构
前面讲了索引的基本原理,数据库的复杂性,又讲了操作系统的相关知识,目的就是让大家了解,任何一种数据结构都不是凭空产生的,一定会有它的背景和使用场景,我们现在总结一下,我们需要这种数据结构能够做些什么,其实很简单,那就是:每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。那么我们就想到如果一个高度可控的多路搜索树是否能满足需求呢?就这样,b+树应运而生(B+树是通过二叉查找树,再由平衡二叉树,B树演化而来)。
如上图,是一颗b+树,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。
###b+树的查找过程
如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。
###b+树性质
1.索引字段要尽量的小:通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。
2.索引的最左匹配特性:当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。
四 聚集索引与辅助索引
在数据库中,B+树的高度一般都在2~4层,这也就是说查找某一个键值的行记录时最多只需要2到4次IO,这倒不错。因为当前一般的机械硬盘每秒至少可以做100次IO,2~4次的IO意味着查询时间只需要0.02~0.04秒。
数据库中的B+树索引可以分为聚集索引(clustered index)和辅助索引(secondary index),
聚集索引与辅助索引相同的是:不管是聚集索引还是辅助索引,其内部都是B+树的形式,即高度是平衡的,叶子结点存放着所有的数据。
聚集索引与辅助索引不同的是:叶子结点存放的是否是一整行的信息
1、聚集索引
#InnoDB存储引擎表示索引组织表,即表中数据按照主键顺序存放。而聚集索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子结点存放的即为整张表的行记录数据,也将聚集索引的叶子结点称为数据页。聚集索引的这个特性决定了索引组织表中数据也是索引的一部分。同B+树数据结构一样,每个数据页都通过一个双向链表来进行链接。 #如果未定义主键,MySQL取第一个唯一索引(unique)而且只含非空列(NOT NULL)作为主键,InnoDB使用它作为聚簇索引。 #如果没有这样的列,InnoDB就自己产生一个这样的ID值,它有六个字节,而且是隐藏的,使其作为聚簇索引。 #由于实际的数据页只能按照一棵B+树进行排序,因此每张表只能拥有一个聚集索引。在多少情况下,查询优化器倾向于采用聚集索引。因为聚集索引能够在B+树索引的叶子节点上直接找到数据。此外由于定义了数据的逻辑顺序,聚集索引能够特别快地访问针对范围值得查询。
聚集索引的好处之一:它对主键的排序查找和范围查找速度非常快,叶子节点的数据就是用户所要查询的数据。如用户需要查找一张表,查询最后的10位用户信息,由于B+树索引是双向链表,所以用户可以快速找到最后一个数据页,并取出10条记录
view code:
#参照第六小结测试索引的准备阶段来创建出表s1 mysql> desc s1; #最开始没有主键 +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | NO | | NULL | | | name | varchar(20) | YES | | NULL | | | gender | char(6) | YES | | NULL | | | email | varchar(50) | YES | | NULL | | +--------+-------------+------+-----+---------+-------+ 4 rows in set (0.00 sec) mysql> explain select * from s1 order by id desc limit 10; #Using filesort,需要二次排序 +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+----------------+ | 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 2633472 | 100.00 | Using filesort | +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+----------------+ 1 row in set, 1 warning (0.11 sec) mysql> alter table s1 add primary key(id); #添加主键 Query OK, 0 rows affected (13.37 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select * from s1 order by id desc limit 10; #基于主键的聚集索引在创建完毕后就已经完成了排序,无需二次排序 +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------+ | 1 | SIMPLE | s1 | NULL | index | NULL | PRIMARY | 4 | NULL | 10 | 100.00 | NULL | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------+ 1 row in set, 1 warning (0.04 sec)
聚集索引的好处之二:范围查询(range query),即如果要查找主键某一范围内的数据,通过叶子节点的上层中间节点就可以得到页的范围,之后直接读取数据页即可
view code:
mysql> alter table s1 drop primary key; Query OK, 2699998 rows affected (24.23 sec) Records: 2699998 Duplicates: 0 Warnings: 0 mysql> desc s1; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | NO | | NULL | | | name | varchar(20) | YES | | NULL | | | gender | char(6) | YES | | NULL | | | email | varchar(50) | YES | | NULL | | +--------+-------------+------+-----+---------+-------+ 4 rows in set (0.12 sec) mysql> explain select * from s1 where id > 1 and id < 1000000; #没有聚集索引,预估需要检索的rows数如下 +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 2690100 | 11.11 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> alter table s1 add primary key(id); Query OK, 0 rows affected (16.25 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select * from s1 where id > 1 and id < 1000000; #有聚集索引,预估需要检索的rows数如下 +----+-------------+-------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+ | 1 | SIMPLE | s1 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 1343355 | 100.00 | Using where | +----+-------------+-------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+ 1 row in set, 1 warning (0.09 sec)
2、辅助索引
表中除了聚集索引外其他索引都是辅助索引(Secondary Index,也称为非聚集索引),与聚集索引的区别是:辅助索引的叶子节点不包含行记录的全部数据。
叶子节点除了包含键值以外,每个叶子节点中的索引行中还包含一个书签(bookmark)。该书签用来告诉InnoDB存储引擎去哪里可以找到与索引相对应的行数据。
由于InnoDB存储引擎是索引组织表,因此InnoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键。如下图
辅助索引的存在并不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引,但只能有一个聚集索引。当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶子级别的指针获得只想主键索引的主键,然后再通过主键索引来找到一个完整的行记录。
举例来说,如果在一棵高度为3的辅助索引树种查找数据,那需要对这个辅助索引树遍历3次找到指定主键,如果聚集索引树的高度同样为3,那么还需要对聚集索引树进行3次查找,最终找到一个完整的行数据所在的页,因此一共需要6次逻辑IO访问才能得到最终的一个数据页。
五 MySQL索引管理
一 功能
#1. 索引的功能就是加速查找 #2. mysql中的primary key,unique,联合唯一也都是索引,这些索引除了加速查找以外,还有约束的功能
二 MySQL常用的索引
普通索引INDEX:加速查找 唯一索引: -主键索引PRIMARY KEY:加速查找+约束(不为空、不能重复) -唯一索引UNIQUE:加速查找+约束(不能重复) 联合索引: -PRIMARY KEY(id,name):联合主键索引 -UNIQUE(id,name):联合唯一索引 -INDEX(id,name):联合普通索引
各个索引的应用场景
举个例子来说,比如你在为某商场做一个会员卡的系统。 这个系统有一个会员表 有下列字段: 会员编号 INT 会员姓名 VARCHAR(10) 会员身份证号码 VARCHAR(18) 会员电话 VARCHAR(10) 会员住址 VARCHAR(50) 会员备注信息 TEXT 那么这个 会员编号,作为主键,使用 PRIMARY 会员姓名 如果要建索引的话,那么就是普通的 INDEX 会员身份证号码 如果要建索引的话,那么可以选择 UNIQUE (唯一的,不允许重复) #除此之外还有全文索引,即FULLTEXT 会员备注信息 , 如果需要建索引的话,可以选择全文搜索。 用于搜索很长一篇文章的时候,效果最好。 用在比较短的文本,如果就一两行字的,普通的 INDEX 也可以。 但其实对于全文搜索,我们并不会使用MySQL自带的该索引,而是会选择第三方软件如Sphinx,专门来做全文搜索。 #其他的如空间索引SPATIAL,了解即可,几乎不用
三 索引的两大类型hash与btree
#我们可以在创建上述索引的时候,为其指定索引类型,分两类 hash类型的索引:查询单条快,范围查询慢 btree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它) #不同的存储引擎支持的索引类型也不一样 InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引; MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引; Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引; NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引; Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;
四 创建/删除索引的语法
#方法一:创建表时 CREATE TABLE 表名 ( 字段名1 数据类型 [完整性约束条件…], 字段名2 数据类型 [完整性约束条件…], [UNIQUE | FULLTEXT | SPATIAL ] INDEX | KEY [索引名] (字段名[(长度)] [ASC |DESC]) ); #方法二:CREATE在已存在的表上创建索引 CREATE [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名 ON 表名 (字段名[(长度)] [ASC |DESC]) ; #方法三:ALTER TABLE在已存在的表上创建索引 ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名 (字段名[(长度)] [ASC |DESC]) ; #删除索引:DROP INDEX 索引名 ON 表名字;
示范:
#方式一 create table t1( id int, name char, age int, sex enum('male','female'), unique key uni_id(id), index ix_name(name) #index没有key ); #方式二 create index ix_age on t1(age); #方式三 alter table t1 add index ix_sex(sex); #查看 mysql> show create table t1; | t1 | CREATE TABLE `t1` ( `id` int(11) DEFAULT NULL, `name` char(1) DEFAULT NULL, `age` int(11) DEFAULT NULL, `sex` enum('male','female') DEFAULT NULL, UNIQUE KEY `uni_id` (`id`), KEY `ix_name` (`name`), KEY `ix_age` (`age`), KEY `ix_sex` (`sex`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1
六 测试索引
一 准备
view code:
#1. 准备表 create table s1( id int, name varchar(20), gender char(6), email varchar(50) ); #2. 创建存储过程,实现批量插入记录 delimiter $$ #声明存储过程的结束符号为$$ create procedure auto_insert1() BEGIN declare i int default 1; while(i<3000000)do insert into s1 values(i,'egon','male',concat('egon',i,'@oldboy')); set i=i+1; end while; END$$ #$$结束 delimiter ; #重新声明分号为结束符号 #3. 查看存储过程 show create procedure auto_insert1\G #4. 调用存储过程 call auto_insert1();
二 在没有索引的前提下测试查询速度
#无索引:mysql根本就不知道到底是否存在id等于333333333的记录,只能把数据表从头到尾扫描一遍,此时有多少个磁盘块就需要进行多少IO操作,所以查询速度很慢 mysql> select * from s1 where id=333333333; Empty set (0.33 sec)
三 在表中已经存在大量数据的前提下,为某个字段段建立索引,建立速度会很慢
四 在索引建立完毕后,以该字段为查询条件时,查询速度提升明显
PS:
1. mysql先去索引表里根据b+树的搜索原理很快搜索到id等于333333333的记录不存在,IO大大降低,因而速度明显提升
2. 我们可以去mysql的data目录下找到该表,可以看到占用的硬盘空间多了
3. 需要注意,如下图
五 总结
#1. 一定是为搜索条件的字段创建索引,比如select * from s1 where id = 333;就需要为id加上索引 #2. 在表中已经有大量数据的情况下,建索引会很慢,且占用硬盘空间,建完后查询速度加快 比如create index idx on s1(id);会扫描表中所有的数据,然后以id为数据项,创建索引结构,存放于硬盘的表中。 建完以后,再查询就会很快了。 #3. 需要注意的是:innodb表的索引会存放于s1.ibd文件中,而myisam表的索引则会有单独的索引文件table1.MYI MySAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在innodb中,表数据文件本身就是按照B+Tree(BTree即Balance True)组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此innodb表数据文件本身就是主索引。 因为inndob的数据文件要按照主键聚集,所以innodb要求表必须要有主键(Myisam可以没有),如果没有显式定义,则mysql系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则mysql会自动为innodb表生成一个隐含字段作为主键,这字段的长度为6个字节,类型为长整型.
七 正确使用索引
一 索引未命中
并不是说我们创建了索引就一定会加快查询速度,若想利用索引达到预想的提高查询速度的效果,我们在添加索引时,必须遵循以下问题
1 范围问题,或者说条件不明确,条件中出现这些符号或关键字:>、>=、<、<=、!= 、between...and...、like、
大于号、小于号
不等于!=
between ...and...
like
2 尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录
#先把表中的索引都删除,让我们专心研究区分度的问题
#先把表中的索引都删除,让我们专心研究区分度的问题 mysql> desc s1; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | YES | MUL | NULL | | | name | varchar(20) | YES | | NULL | | | gender | char(5) | YES | | NULL | | | email | varchar(50) | YES | MUL | NULL | | +--------+-------------+------+-----+---------+-------+ 4 rows in set (0.00 sec) mysql> drop index a on s1; Query OK, 0 rows affected (0.20 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> drop index d on s1; Query OK, 0 rows affected (0.18 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> desc s1; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | | name | varchar(20) | YES | | NULL | | | gender | char(5) | YES | | NULL | | | email | varchar(50) | YES | | NULL | | +--------+-------------+------+-----+---------+-------+ 4 rows in set (0.00 sec)
分析原因
我们编写存储过程为表s1批量添加记录,name字段的值均为egon,也就是说name这个字段的区分度很低(gender字段也是一样的,我们稍后再搭理它) 回忆b+树的结构,查询的速度与树的高度成反比,要想将树的高低控制的很低,需要保证:在某一层内数据项均是按照从左到右,从小到大的顺序依次排开,即左1<左2<左3<... 而对于区分度低的字段,无法找到大小关系,因为值都是相等的,毫无疑问,还想要用b+树存放这些等值的数据,只能增加树的高度,字段的区分度越低,则树的高度越高。极端的情况,索引字段的值都一样,那么b+树几乎成了一根棍。本例中就是这种极端的情况,name字段所有的值均为'egon' #现在我们得出一个结论:为区分度低的字段建立索引,索引树的高度会很高,然而这具体会带来什么影响呢??? #1:如果条件是name='xxxx',那么肯定是可以第一时间判断出'xxxx'是不在索引树中的(因为树中所有的值均为'egon’),所以查询速度很快 #2:如果条件正好是name='egon',查询时,我们永远无法从树的某个位置得到一个明确的范围,只能往下找,往下找,往下找。。。这与全表扫描的IO次数没有多大区别,所以速度很慢
3 =和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
4 索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’)
5 and/or
#1、and与or的逻辑 条件1 and 条件2:所有条件都成立才算成立,但凡要有一个条件不成立则最终结果不成立 条件1 or 条件2:只要有一个条件成立则最终结果就成立 #2、and的工作原理 条件: a = 10 and b = 'xxx' and c > 3 and d =4 索引: 制作联合索引(d,a,b,c) 工作原理: 对于连续多个and:mysql会按照联合索引,从左到右的顺序找一个区分度高的索引字段(这样便可以快速锁定很小的范围),加速查询,即按照d—>a->b->c的顺序 #3、or的工作原理 条件: a = 10 or b = 'xxx' or c > 3 or d =4 索引: 制作联合索引(d,a,b,c) 工作原理: 对于连续多个or:mysql会按照条件的顺序,从左到右依次判断,即a->b->c->d
在左边条件成立但是索引字段的区分度低的情况下(name与gender均属于这种情况),会依次往右找到一个区分度高的索引字段,加速查询
经过分析,在条件为name='egon' and gender='male' and id>333 and email='xxx'的情况下,我们完全没必要为前三个条件的字段加索引,因为只能用上email字段的索引,前三个字段的索引反而会降低我们的查询效率
6 最左前缀匹配原则(详见第八小节),非常重要的原则,对于组合索引mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配(指的是范围大了,有索引速度也慢),比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
7 其他情况
view code:
- 使用函数 select * from tb1 where reverse(email) = 'egon'; - 类型不一致 如果列是字符串类型,传入条件是必须用引号引起来,不然... select * from tb1 where email = 999; #排序条件为索引,则select字段必须也是索引字段,否则无法命中 - order by select name from s1 order by email desc; 当根据索引排序时候,select查询的字段如果不是索引,则速度仍然很慢 select email from s1 order by email desc; 特别的:如果对主键排序,则还是速度很快: select * from tb1 order by nid desc; - 组合索引最左前缀 如果组合索引为:(name,email) name and email -- 命中索引 name -- 命中索引 email -- 未命中索引 - count(1)或count(列)代替count(*)在mysql中没有差别了 - create index xxxx on tb(title(19)) #text类型,必须制定长度
二 其他注意事项
- 避免使用select * - count(1)或count(列) 代替 count(*) - 创建表时尽量时 char 代替 varchar - 表的字段顺序固定长度的字段优先 - 组合索引代替多个单列索引(经常使用多个条件查询时) - 尽量使用短索引 - 使用连接(JOIN)来代替子查询(Sub-Queries) - 连表时注意条件类型需一致 - 索引散列值(重复少)不适合建索引,例:性别不适合
八 联合索引与覆盖索引
一 联合索引
联合索引时指对表上的多个列合起来做一个索引。联合索引的创建方法与单个索引的创建方法一样,不同之处在仅在于有多个索引列,如下
mysql> create table t( -> a int, -> b int, -> primary key(a), -> key idx_a_b(a,b) -> ); Query OK, 0 rows affected (0.11 sec)
那么何时需要使用联合索引呢?在讨论这个问题之前,先来看一下联合索引内部的结果。从本质上来说,联合索引就是一棵B+树,不同的是联合索引的键值得数量不是1,而是>=2。接着来讨论两个整型列组成的联合索引,假定两个键值得名称分别为a、b如图
可以看到这与我们之前看到的单个键的B+树并没有什么不同,键值都是排序的,通过叶子结点可以逻辑上顺序地读出所有数据,就上面的例子来说,即(1,1),(1,2),(2,1),(2,4),(3,1),(3,2),数据按(a,b)的顺序进行了存放。
因此,对于查询select * from table where a=xxx and b=xxx, 显然是可以使用(a,b) 这个联合索引的,对于单个列a的查询select * from table where a=xxx,也是可以使用(a,b)这个索引的。
但对于b列的查询select * from table where b=xxx,则不可以使用(a,b) 索引,其实你不难发现原因,叶子节点上b的值为1、2、1、4、1、2显然不是排序的,因此对于b列的查询使用不到(a,b) 索引
联合索引的第二个好处是在第一个键相同的情况下,已经对第二个键进行了排序处理,例如在很多情况下应用程序都需要查询某个用户的购物情况,并按照时间进行排序,最后取出最近三次的购买记录,这时使用联合索引可以帮我们避免多一次的排序操作,因为索引本身在叶子节点已经排序了,如下
#===========准备表============== create table buy_log( userid int unsigned not null, buy_date date ); insert into buy_log values (1,'2009-01-01'), (2,'2009-01-01'), (3,'2009-01-01'), (1,'2009-02-01'), (3,'2009-02-01'), (1,'2009-03-01'), (1,'2009-04-01'); alter table buy_log add key(userid); alter table buy_log add key(userid,buy_date); #===========验证============== mysql> show create table buy_log; | buy_log | CREATE TABLE `buy_log` ( `userid` int(10) unsigned NOT NULL, `buy_date` date DEFAULT NULL, KEY `userid` (`userid`), KEY `userid_2` (`userid`,`buy_date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 | #可以看到possible_keys在这里有两个索引可以用,分别是单个索引userid与联合索引userid_2,但是优化器最终选择了使用的key是userid因为该索引的叶子节点包含单个键值,所以理论上一个页能存放的记录应该更多 mysql> explain select * from buy_log where userid=2; +----+-------------+---------+------+-----------------+--------+---------+-------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+-----------------+--------+---------+-------+------+-------+ | 1 | SIMPLE | buy_log | ref | userid,userid_2 | userid | 4 | const | 1 | | +----+-------------+---------+------+-----------------+--------+---------+-------+------+-------+ 1 row in set (0.00 sec) #接着假定要取出userid为1的最近3次的购买记录,用的就是联合索引userid_2了,因为在这个索引中,在userid=1的情况下,buy_date都已经排序好了 mysql> explain select * from buy_log where userid=1 order by buy_date desc limit 3; +----+-------------+---------+------+-----------------+----------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+-----------------+----------+---------+-------+------+--------------------------+ | 1 | SIMPLE | buy_log | ref | userid,userid_2 | userid_2 | 4 | const | 4 | Using where; Using index | +----+-------------+---------+------+-----------------+----------+---------+-------+------+--------------------------+ 1 row in set (0.00 sec) #ps:如果extra的排序显示是Using filesort,则意味着在查出数据后需要二次排序 #对于联合索引(a,b),下述语句可以直接使用该索引,无需二次排序 select ... from table where a=xxx order by b; #然后对于联合索引(a,b,c)来首,下列语句同样可以直接通过索引得到结果 select ... from table where a=xxx order by b; select ... from table where a=xxx and b=xxx order by c; #但是对于联合索引(a,b,c),下列语句不能通过索引直接得到结果,还需要自己执行一次filesort操作,因为索引(a,c)并未排序 select ... from table where a=xxx order by c;
二 覆盖索引
InnoDB存储引擎支持覆盖索引(covering index,或称索引覆盖),即从辅助索引中就可以得到查询记录,而不需要查询聚集索引中的记录。
使用覆盖索引的一个好处是:辅助索引不包含整行记录的所有信息,故其大小要远小于聚集索引,因此可以减少大量的IO操作
注意:覆盖索引技术最早是在InnoDB Plugin中完成并实现,这意味着对于InnoDB版本小于1.0的,或者MySQL数据库版本为5.0以下的,InnoDB存储引擎不支持覆盖索引特性
对于InnoDB存储引擎的辅助索引而言,由于其包含了主键信息,因此其叶子节点存放的数据为(primary key1,priamey key2,...,key1,key2,...)。例如
select age from s1 where id=123 and name = 'egon'; #id字段有索引,但是name字段没有索引,该sql命中了索引,但未覆盖,需要去聚集索引中再查找详细信息。 最牛逼的情况是,索引字段覆盖了所有,那全程通过索引来加速查询以及获取结果就ok了 mysql> desc s1; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | NO | | NULL | | | name | varchar(20) | YES | | NULL | | | gender | char(6) | YES | | NULL | | | email | varchar(50) | YES | | NULL | | +--------+-------------+------+-----+---------+-------+ 4 rows in set (0.21 sec) mysql> explain select name from s1 where id=1000; #没有任何索引 +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 2688336 | 10.00 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> create index idx_id on s1(id); #创建索引 Query OK, 0 rows affected (4.16 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select name from s1 where id=1000; #命中辅助索引,但是未覆盖索引,还需要从聚集索引中查找name +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+ | 1 | SIMPLE | s1 | NULL | ref | idx_id | idx_id | 4 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.08 sec) mysql> explain select id from s1 where id=1000; #在辅助索引中就找到了全部信息,Using index代表覆盖索引 +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | s1 | NULL | ref | idx_id | idx_id | 4 | const | 1 | 100.00 | Using index | +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (0.03 sec)
覆盖索引的另外一个好处是对某些统计问题而言的。基于上一小结创建的表buy_log,查询计划如下
mysql> explain select count(*) from buy_log; +----+-------------+---------+-------+---------------+--------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+--------+---------+------+------+-------------+ | 1 | SIMPLE | buy_log | index | NULL | userid | 4 | NULL | 7 | Using index | +----+-------------+---------+-------+---------------+--------+---------+------+------+-------------+ 1 row in set (0.00 sec)
innodb存储引擎并不会选择通过查询聚集索引来进行统计。由于buy_log表有辅助索引,而辅助索引远小于聚集索引,选择辅助索引可以减少IO操作,故优化器的选择如上key为userid辅助索引
对于(a,b)形式的联合索引,一般是不可以选择b中所谓的查询条件。但如果是统计操作,并且是覆盖索引,则优化器还是会选择使用该索引,如下
#联合索引userid_2(userid,buy_date),一般情况,我们按照buy_date是无法使用该索引的,但特殊情况下:查询语句是统计操作,且是覆盖索引,则按照buy_date当做查询条件时,也可以使用该联合索引 mysql> explain select count(*) from buy_log where buy_date >= '2011-01-01' and buy_date < '2011-02-01'; +----+-------------+---------+-------+---------------+----------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+----------+---------+------+------+--------------------------+ | 1 | SIMPLE | buy_log | index | NULL | userid_2 | 8 | NULL | 7 | Using where; Using index | +----+-------------+---------+-------+---------------+----------+---------+------+------+--------------------------+ 1 row in set (0.00 sec)
九 查询优化神器-explain
关于explain命令相信大家并不陌生,具体用法和字段含义可以参考官网explain-output,这里需要强调rows是核心指标,绝大部分rows小的语句执行一定很快(有例外,下面会讲到)。所以优化语句基本上都是在优化rows。
执行计划:让mysql预估执行操作(一般正确) all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const id,email 慢: select * from userinfo3 where name='alex' explain select * from userinfo3 where name='alex' type: ALL(全表扫描) select * from userinfo3 limit 1; 快: select * from userinfo3 where email='alex' type: const(走索引)
http://blog.itpub.net/29773961/viewspace-1767044/
十 慢查询优化的基本步骤
0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE 1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高 2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询) 3.order by limit 形式的sql语句让排序的表优先查 4.了解业务方使用场景 5.加索引时参照建索引的几大原则 6.观察结果,不符合预期继续从0分析
十一 慢日志管理
慢日志 - 执行时间 > 10 - 未命中索引 - 日志文件路径 配置: - 内存 show variables like '%query%'; show variables like '%queries%'; set global 变量名 = 值 - 配置文件 mysqld --defaults-file='E:\wupeiqi\mysql-5.7.16-winx64\mysql-5.7.16-winx64\my-default.ini' my.conf内容: slow_query_log = ON slow_query_log_file = D:/.... 注意:修改配置文件之后,需要重启服务
日志管理:
MySQL日志管理 ======================================================== 错误日志: 记录 MySQL 服务器启动、关闭及运行错误等信息 二进制日志: 又称binlog日志,以二进制文件的方式记录数据库中除 SELECT 以外的操作 查询日志: 记录查询的信息 慢查询日志: 记录执行时间超过指定时间的操作 中继日志: 备库将主库的二进制日志复制到自己的中继日志中,从而在本地进行重放 通用日志: 审计哪个账号、在哪个时段、做了哪些事件 事务日志或称redo日志: 记录Innodb事务相关的如事务执行时间、检查点等 ======================================================== 一、bin-log 1. 启用 # vim /etc/my.cnf [mysqld] log-bin[=dir\[filename]] # service mysqld restart 2. 暂停 //仅当前会话 SET SQL_LOG_BIN=0; SET SQL_LOG_BIN=1; 3. 查看 查看全部: # mysqlbinlog mysql.000002 按时间: # mysqlbinlog mysql.000002 --start-datetime="2012-12-05 10:02:56" # mysqlbinlog mysql.000002 --stop-datetime="2012-12-05 11:02:54" # mysqlbinlog mysql.000002 --start-datetime="2012-12-05 10:02:56" --stop-datetime="2012-12-05 11:02:54" 按字节数: # mysqlbinlog mysql.000002 --start-position=260 # mysqlbinlog mysql.000002 --stop-position=260 # mysqlbinlog mysql.000002 --start-position=260 --stop-position=930 4. 截断bin-log(产生新的bin-log文件) a. 重启mysql服务器 b. # mysql -uroot -p123 -e 'flush logs' 5. 删除bin-log文件 # mysql -uroot -p123 -e 'reset master' 二、查询日志 启用通用查询日志 # vim /etc/my.cnf [mysqld] log[=dir\[filename]] # service mysqld restart 三、慢查询日志 启用慢查询日志 # vim /etc/my.cnf [mysqld] log-slow-queries[=dir\[filename]] long_query_time=n # service mysqld restart MySQL 5.6: slow-query-log=1 slow-query-log-file=slow.log long_query_time=3 查看慢查询日志 测试:BENCHMARK(count,expr) SELECT BENCHMARK(50000000,2*3);