数据库 第八章

第八章 MySQL事务和SQL优化

8.1 什么是事务

Transaction,一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)。一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成。事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同。

数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成

以银行转账为例:账户转账是一个完整的业务,最小的单元,不可再分——也就是说银行账 户转账是一个事务:

update t_act set balance=balance - 400 where actno=1;
update t_act set balance=balance + 400 where actno=2;

以上两台DML语句必须同时成功或者同时失败。最小单元不可再分,当第一条DML语句执行成功后,并不能将底层数据库中的第一个账户的数据修改,只是将操作记录了一下;这个记录是在内存中完成的;当第二条DML语句执行成功后,和底层数据库文件中的数据完成同步。若第二条DML语句执行失败,则清空所有的历史操作记录,要完成以上的功能必须借助事务。

事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。

事务处理(事务操作):保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;

要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。

为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。

8.2 事务的特性

事务是由一组SQL语句 组成的逻辑处理单元,它的ACID特性如下:

**原子性(Atomicity):**原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

**一致性(Consistency):**事务必须使数据库从一个一致性状态变换到另外一个一致性状态。

**隔离性(Isolation):**事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

**持久性(Durability):**持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

image-20211013150740210

8.3 MySQL使用事务

MySQL开启事务、回滚事务、提交事务命令。

begin
说明:在5.5 以上的版本,不需要手工begin,只要你执行的是一个DML,会自动在前面加一个begin命令。

commit:提交事务
完成一个事务,一旦事务提交成功 ,就说明具备ACID特性了。

rollback :回滚事务
将内存中,已执行过的操作,回滚回去

自动提交策略:

MySQL默认已经开启自动提交,我们可以通过对应的设置来开启或者关闭自动提交。

db01 [(none)]>select @@autocommit;
db01 [(none)]>set autocommit=0;


注:
自动提交是否打开,一般在有事务需求的MySQL中,将其关闭
不管有没有事务需求,我们一般也都建议设置为0,可以很大程度上提高数据库性能

(1)
set autocommit=0;   

(2)
vim /etc/my.cnf
autocommit=0  

隐式提交语句:

用于隐式提交的 SQL 语句:
begin 
a
b
begin
SET AUTOCOMMIT = 1

导致提交的非事务语句:
DDL语句: (ALTER、CREATE 和 DROP)
DCL语句: (GRANT、REVOKE 和 SET PASSWORD)
锁定语句:(LOCK TABLES 和 UNLOCK TABLES)

导致隐式提交的语句示例:
TRUNCATE TABLE
LOAD DATA INFILE
SELECT FOR UPDATE

开始事务流程:

1、检查autocommit是否为关闭状态
select @@autocommit;
或者:
#个别版本不支持
show variables like 'autocommit';

2、开启事务,并结束事务

BEGIN;
DELETE FROM student WHERE NAME='alexsb';
UPDATE student SET NAME='alexsb' WHERE NAME='alex';
ROLLBACK;

BEGIN;
DELETE FROM student WHERE NAME='alexsb';
UPDATE student SET NAME='alexsb' WHERE NAME='alex';
COMMIT;

8.4 InnoDB 事务的ACID如何保证

8.4.1 基本概念

redo log ---> 重做日志 ib_logfile0~1   50M   , 轮询使用

redo log buffer ---> redo内存区域

ibd     ----> 存储 数据行和索引 

buffer pool --->缓冲区池,数据和索引的缓冲

LSN : 日志序列号 

磁盘数据页,redo文件,buffer pool,redo buffer
MySQL 每次数据库启动,都会比较磁盘数据页和redolog的LSN,必须要求两者LSN一致数据库才能正常启动
WAL : write ahead log 日志优先写的方式实现持久化
脏页: 内存脏页,内存中发生了修改,没写入到磁盘之前,我们把内存页称之为脏页.
CKPT:Checkpoint,检查点,就是将脏页刷写到磁盘的动作
TXID: 事务号,InnoDB会为每一个事务生成一个事务号,伴随着整个事务
ACSR(Auto Crash Safey Recovery)自动的故障安全恢复

8.4.2 redo log

Redo是什么?

redo,顾名思义“重做日志”,是事务日志的一种。

作用是什么?

在事务ACID过程中,实现的是“D”持久化的作用。对于AC也有相应的作用

redo日志位置

redo的日志文件:iblogfile0 iblogfile1

redo buffer

redo的buffer:数据页的变化信息+数据页当时的LSN号

LSN:日志序列号  磁盘数据页、内存数据页、redo buffer、redolog

redo的刷新策略

commit;
刷新当前事务的redo buffer到磁盘
还会顺便将一部分redo buffer中没有提交的事务日志也刷新到磁盘

MySQL CSR——前滚

MySQL : 在启动时,必须保证redo日志文件和数据文件LSN必须一致, 如果不一致就会触发CSR,最终保证一致

情况一:
我们做了一个事务,begin;update;commit.
1. 在begin ,会立即分配一个TXID=tx_01.
2. update时,会将需要修改的数据页(dp_01,LSN=101),加载到data buffer中
3. DBWR线程,会进行dp_01数据页修改更新,并更新LSN=102
4. LOGBWR日志写线程,会将dp_01数据页的变化+LSN+TXID存储到redobuffer
5. 执行commit时,LGWR日志写线程会将redobuffer信息写入redolog日志文件中,基于WAL原则,在日志完全写入磁盘后,commit命令才执行成功,(会将此日志打上commit标记)
6. 假如此时宕机,内存脏页没有来得及写入磁盘,内存数据全部丢失
7. MySQL再次重启时,必须要redolog和磁盘数据页的LSN是一致的.但是,此时dp_01,TXID=tx_01磁盘是
LSN=101,dp_01,TXID=tx_01,redolog中LSN=102

MySQL此时无法正常启动,MySQL触发CSR.在内存追平LSN号,触发ckpt,将内存数据页更新到磁盘,从而保证磁盘数据页和redolog LSN一值.这时MySQL正常启动

以上的工作过程,我们把它称之为基于REDO的"前滚操作"

8.4.3 undo 回滚日志

undo是什么?

undo,顾名思义“回滚日志”

作用是什么?

在事务ACID过程中,实现的是“A” 原子性的作用
另外CI也依赖于Undo
在rolback时,将数据恢复到修改之前的状态
在CSR实现的是,将redo当中记录的未提交的时候进行回滚.
undo提供快照技术,保存事务修改之前的数据状态.保证了MVCC,隔离性,mysqldump的热备

对于使用 InnoDB 存储引擎的表来说,它的聚簇索引记录中都包含 3 个隐藏列

db_row_id:隐藏的行 ID。在没有自定义主键也没有 Unique 键的情况下,会使用该隐藏列作为主键。

db_trx_id:操作这个数据的事务 ID,也就是最后一个对该数据进行插入或更新的事务 ID。

db_roll_ptr:回滚指针,也就是指向这个记录的 Undo Log 信息。Undo Log 中存储了回滚需要的数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lEkI4GXT-1666950266198)(D:\微服务\Java课程\鸥鹏标准化\鸥鹏MySql数据库标准化\mysql-images\image-20221025170250372.png)]

8.5 事务的隔离级别

多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。

如果不考虑隔离性,可能会引发如下问题:

1、幻想读

2、不可重复读取

3、脏读

8.5.1 脏读

指一个事务读取了另外一个事务未提交的数据。

这是非常危险的,假设B向A转帐100元,对应sql语句如下所示
1. update account set money=money+100 where name=‘a’;    
2. update account set money=money-100 where name=‘b’;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LOqlqocc-1666950266199)(D:\微服务\Java课程\鸥鹏标准化\鸥鹏MySql数据库标准化\mysql-images\2022-10-25_171552.png)]

解决 : 串行化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3JH2spou-1666950266200)(D:\微服务\Java课程\鸥鹏标准化\鸥鹏MySql数据库标准化\mysql-images\2022-10-25_173507.png)]

8.5.2 不可重复读

不可重复读,是指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。

这是由于查询时系统中其他事务修改的提交而引起的。比如事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。

一种更易理解的说法是:在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据并修改数据。那么,在第一个事务的两次读数据之间。由于另一个事务的修改,那么第一个事务两次读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B3iHabcj-1666950266200)(D:\微服务\Java课程\鸥鹏标准化\鸥鹏MySql数据库标准化\mysql-images\2022-10-25_172402.png)]

8.5.3 虚读/幻读

是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。

如:事务A 按照一定条件进行数据读取, 期间事务B 插入了相同搜索条件的新数据,事务A再次按照原先条件进行读取时,发现了事务B 新插入的数据 称为幻读。

幻读和不可重复读的区别:

不可重复读:是同一条SQL查询的内容不同(被修改了)

幻读是:查询的结果条数不同(增加了、或者删除了记录)

幻读的影响

会造成一个事务中先产生的锁,无法锁住后加入的满足条件的行。

产生数据一致性问题,在一个事务中,先对符合条件的目标行做变更,而在事务提交前有新的符合目标条件的行加入。这样通过binlog恢复的数据是会将所有符合条件的目标行都进行变更的。

幻读产生的原因

行锁只能锁住行,即使把所有的行记录都上锁,也阻止不了新插入的记录。

如何解决幻读
  • 将两行记录间的空隙加上锁,阻止新记录的插入;这个锁称为间隙锁
  • 间隙锁与间隙锁之间没有冲突关系。跟间隙锁存在冲突关系的,是往这个间隙中插入一个记录这个操作。

8.5.4 事务的隔离级别

为了处理这些问题,SQL标准定义了以下几种事务隔离级别

脏读不可重复读幻读
Read uncommitted
Read committed×
Repeatable read××
Serializable×xx

数据库共定义了四种隔离级别:

  • Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)

  • Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)

  • Read committed:可避免脏读情况发生(读已提交)。

  • Read uncommitted:最低级别,以上情况均无法保证。(读未提交)

可以通过命令 set transaction命令设置事务隔离级别:

语法 : set transaction isolation level 设置事务隔离级别


select @@tx_isolation   查询当前事务隔离级别

SET  TRANSACTION ISOLATION LEVEL SERIALIZABLE;

select @@tx_isolation

SET  TRANSACTION ISOLATION LEVEL REPEATABLE READ;

select @@tx_isolation

8.6 MySQL优化方式

系统优化:硬件、架构

  • 服务优化
  • 应用优化

影响性能的因素

  • 应用程序

  • 查询

  • 事务管理

  • 数据库设计

  • 数据分布

  • 网络

  • 操作系统

  • 硬件

谁参与优化

数据库管理员
业务部门代表
应用程序架构师
应用程序设计人员
应用程序开发人员
硬件及系统管理员
存储管理员

1、系统优化

1、硬件优化
    cpu 64位 一台机器8-16颗CPU
    内存 96-128G 3-4个实例
    硬盘:数量越多越好 性能:ssd(高并发业务) > sas (普通业务)>sata(线下业务)
    raid(独立磁盘冗余阵列) 性能 raid0 > raid10 > raid5 > raid1
    网卡:多块网卡bond 
2、软件优化
    操作系统:使用64位系统
    软件:MySQL 编译优化

SATA

SATA的全称是Serial Advanced Technology Attachment,是由Intel、IBM、Dell、APT、Maxtor和Seagate公司共同提出的硬盘接口规范。

SAS

从接口标准上而言,SATA是SAS的一个子标准,因此SAS控制器可以直接操控SATA硬盘,但是SAS却不能直接使用在SATA的环境中,因为SATA控制器并不能对SAS硬盘进行控制

SSD

SSD是摒弃传统磁介质,采用电子存储介质进行数据存储和读取的一种技术,突破了传统机械硬盘的性能瓶颈,拥有极高的存储性能,被认为是存储技术发展的未来新星。

RAID是英文Redundant Array of IndependentDisks的缩写,中文简称为。RAID就是一种由多块硬盘构成的冗余阵列。虽然RAID包含多块硬盘,但是在操作系统下是作为一个独立的大型存储设备出现。RAID4即带奇偶校验码的独立磁盘结构

2、服务优化

MySQL配置原则
    配置合理的MySQL服务器,尽量在应用本身达到一个MySQL最合理的使用
    针对 MyISAM 或 InnoDB 不同引擎进行不同定制性配置
    针对不同的应用情况进行合理配置
    针对 my.cnf 进行配置,后面设置是针对内存为2G的服务器进行的合理设置

公共选项:

选项缺省值推荐值说明
max_connections1001024MySQL服务器同时处理的数据库连接的最大数量
query_cache_size0 (不开启)16M查询缓存区的最大长度,按照当前需求,一倍一倍增加,本选项比较重要
sort_buffer_size512K16M每个线程的排序缓存大小,一般按照内存可以设置为2M以上,推荐是16M,该选项对排序order by,group by起作用
record_buffer128K16M每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区,可以设置为2M以上
table_cache64512为所有线程打开表的数量。增加该值能增加mysqld要求的文件描述符的数量。MySQL对每个唯一打开的表需要2个文件描述符。

MyISAM 选项:

选项缺省值推荐值说明
key_buffer_size8M256M用来存放索引区块的缓存值, 建议128M以上,不要大于内存的30%
read_buffer_size128K16M用来做MyISAM表全表扫描的缓冲大小. 为从数据表顺序读取数据的读操作保留的缓存区的长度
myisam_sort_buffer_size16M128M设置,恢复,修改表的时候使用的缓冲大小,值不要设的太大

InnoDB 选项:

选项缺省值推荐值说明
innodb_buffer_pool_size32M1GInnoDB使用一个缓冲池来保存索引和原始数据, 这里你设置越大,你在存取表里面数据时所需要的磁盘I/O越少,一般是内存的一半,不超过2G,否则系统会崩溃,这个参数非常重要
innodb_additional_mem_pool_size2M128MInnoDB用来保存 metadata 信息,如果内存是4G,最好本值超过200M
innodb_flush_log_at_trx_commit100 代表日志只大约每秒写入日志文件并且日志文件刷新到磁盘; 1 为执行完没执行一条SQL马上commit; 2代表日志写入日志文件在每次提交后,但是日志文件只有大约每秒才会刷新到磁盘上. 对速度影响比较大,同时也关系数据完整性
innodb_log_file_size8M256M在日志组中每个日志文件的大小, 一般是innodb_buffer_pool_size的25%,官方推荐是innodb_buffer_pool_size 的 40-50%, 设置大一点来避免在日志文件覆写上不必要的缓冲池刷新行为
innodb_log_buffer_size128K8M用来缓冲日志数据的缓冲区的大小.推荐是8M,官方推荐该值小于16M,最好是 1M-8M 之间

参数优化 :

Max_connections

(1)简介
Mysql的最大连接数,如果服务器的并发请求量比较大,可以调高这个值,当然这是要建立在机器能够支撑的
情况下,因为如果连接数越来越多,mysql会为每个连接提供缓冲区,就会开销越多的内存,所以需要适当
的调整该值,不能随便去提高设值。
(2)判断依据
show variables like 'max_connections';
    +-----------------+-------+
    | Variable_name   | Value |
    +-----------------+-------+
    | max_connections | 151   |
    +-----------------+-------+
    
show status like 'Max_used_connections';
    +----------------------+-------+
    | Variable_name        | Value |
    +----------------------+-------+
    | Max_used_connections | 101   |
     +----------------------+-------+
(3)修改方式举例
vim /etc/my.cnf 
Max_connections=1024
 

back_log

(1)简介
mysql能暂存的连接数量,当主要mysql线程在一个很短时间内得到非常多的连接请求时候它就会起作用,如
果mysql的连接数据达到max_connections时候,新来的请求将会被存在堆栈中,等待某一连接释放资源,
该堆栈的数量即back_log,如果等待连接的数量超过back_log,将不被授予连接资源。
back_log值指出在mysql暂时停止回答新请求之前的短时间内有多少个请求可以被存在堆栈中,只有如果期
望在一个短时间内有很多连接的时候需要增加它
(2)判断依据
SHOW FULL PROCESSLIST;
发现大量的待连接进程时,就需要加大back_log或者加大max_connections的值
(3)修改方式举例
vim /etc/my.cnf 
back_log=1024

wait_timeout和interactive_timeout

(1)简介
wait_timeout:指的是mysql在关闭一个非交互的连接之前所要等待的秒数
interactive_timeout:指的是mysql在关闭一个交互的连接之前所需要等待的秒数,比如我们在终端上进
行mysql管理,使用的即使交互的连接,这时候,如果没有操作的时间超过了interactive_time设置的时
间就会自动的断开,默认的是28800,可调优为7200。
wait_timeout:如果设置太小,那么连接关闭的就很快,从而使一些持久的连接不起作用
(2)设置建议
如果设置太大,容易造成连接打开时间过长,在show processlist时候,能看到很多的连接 ,一般希望
wait_timeout尽可能低
(3)修改方式举例
wait_timeout=60
interactive_timeout=1200
长连接的应用,为了不去反复的回收和分配资源,降低额外的开销。
一般我们会将wait_timeout设定比较小,interactive_timeout要和应用开发人员沟通长链接的应用是
否很多。如果他需要长链接,那么这个值可以不需要调整。

max_allowed_packet

(1)简介:
mysql根据配置文件会限制,server接受的数据包大小。
(2)配置依据:
有时候大的插入和更新会受max_allowed_packet参数限制,导致写入或者更新失败,更大值是1GB,必须
设置1024的倍数
(3)配置方法:
max_allowed_packet=32M

参数优化示例:

[mysqld]
basedir=/data/mysql
datadir=/data/mysql/data
socket=/tmp/mysql.sock
log-error=/var/log/mysql.log
log_bin=/data/binlog/mysql-bin
binlog_format=row
skip-name-resolve
server-id=52
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
relay_log_purge=0
max_connections=1024
back_log=128
wait_timeout=60
interactive_timeout=7200
key_buffer_size=16M
query_cache_size=64M
query_cache_type=1
query_cache_limit=50M
max_connect_errors=20
sort_buffer_size=2M
max_allowed_packet=32M
join_buffer_size=2M
thread_cache_size=200
innodb_buffer_pool_size=1024M
innodb_flush_log_at_trx_commit=1
innodb_log_buffer_size=32M
innodb_log_file_size=128M
innodb_log_files_in_group=3
binlog_cache_size=2M
max_binlog_cache_size=8M
max_binlog_size=512M
expire_logs_days=7
read_buffer_size=2M
read_rnd_buffer_size=2M
bulk_insert_buffer_size=8M
[client]
socket=/tmp/mysql.sock 

压力测试 :

 # 注意先创建数据库db1,并创建表t_100w,添加记录k2='FGCD'
 mysqlslap --defaults-file=/etc/my.cnf --concurrency=100 --iterations=1 --create-schema='db1' --query="select * from db1.t_100w where k2='FGCD'" engine=innodb --number-of-queries=200000 -uroot -proot -verbose

–concurrency 并发数量
–iterations 运行这些测试多少次
–number-of-queries 总共要运行多少次查询。每个客户运行的查询数量可以用查询总数/并发数来计算
-verbose 引用的配置

Benchmark
Running for engine rbose
Average number of seconds to run all queries: 10.454 seconds
Minimum number of seconds to run all queries: 10.454 seconds
Maximum number of seconds to run all queries: 10.454 seconds
Number of clients running queries: 100
Average number of queries per client: 2000

3、应用优化

设计合理的数据表结构:适当的数据冗余
对数据表建立合适有效的数据库索引
数据查询:编写简洁高效的SQL语句

表结构设计原则
    选择合适的数据类型:如果能够定长尽量定长
    使用 ENUM 而不是 VARCHAR,ENUM类型是非常快和紧凑的,在实际上,其保存的是 TINYINT,但其
    外表上显示为字符串。这样一来,用这个字段来做一些选项列表变得相当的完美 。
    不要使用无法加索引的类型作为关键字段,比如 text类型
    为了避免联表查询,有时候可以适当的数据冗余,比如邮箱、姓名这些不容易更改的数据
    选择合适的表引擎,有时候 MyISAM 适合,有时候InnoDB适合
    为保证查询性能,最好每个表都建立有 auto_increment 字段, 建立合适的数据库索引
    最好给每个字段都设定 default 值
    
索引建立原则(一)
    一般针对数据分散的关键字进行建立索引,比如ID、QQ,•  
    像性别、状态值等等建立索引没有意义字段唯一,最少,不可为null
     对大数据量表建立聚集索引,避免更新操作带来的碎片。
     尽量使用短索引,一般对int、char/varchar、date/time 等类型的字段建立索引
     需要的时候建立联合索引,但是要注意查询SQL语句的编写
     谨慎建立 unique 类型的索引(唯一索引)
     大文本字段不建立为索引,如果要对大文本字段进行检索,
     可以考虑全文索引
     频繁更新的列不适合建立索引
     
索引建立原则(二)
    order by 字句中的字段,where 子句中字段,最常用的sql语句中字段,应建立索引。           
        
    唯一性约束,系统将默认为该字段建立索引。
    对于只是做查询用的数据库索引越多越好,但对于在线实时系统建议控制在5个以内。
    索引不仅能提高查询SQL性能,同时也可以提高带where字句的update,Delete SQL性能。
    Decimal 类型字段不要单独建立为索引,但覆盖索引可以包含这些字段。
    只有建立索引以后,表内的行才按照特地的顺序存储,按照需要可以是asc或desc方式。
    如果索引由多个字段组成将最用来查询过滤的字段放在前面可能会有更好的性能。
    
编写高效的 SQL (一)
    能够快速缩小结果集的 WHERE 条件写在前面,如果有恒量条件,也尽量放在前面。
    尽量避免使用 GROUP BY、DISTINCT 、OR、IN 等语句的使用,避免使用联表查询和子查询,
    因为将使执行效率大大下降。
    能够使用索引的字段尽量进行有效的合理排列,如果使用了联合索引,请注意提取字段的前后顺序。
    针对索引字段使用 >, >=, =, <, <=, IF NULL和BETWEEN 将会使用索引,
    如果对某个索引字段进行 LIKE 查询,使用 LIKE  ‘%abc%’不能使用索引,使用 LIKE ‘abc%’
    将能够使用索引    如果在SQL里使用了MySQL部分自带函数,索引将失效,
    同时将无法使用 MySQL 的 Query Cache。
    比如 LEFT(), SUBSTR(), TO_DAYS(),DATE_FORMAT(), 等,如果使用了 OR 或 IN,索引也将失效
    使用 Explain 语句来帮助改进我们的SQL语句
    
    
编写高效的 SQL (二)
    不要在where 子句中的“=”左边进行算术或表达式运算,否则系统将可能无法正确使用索引
    尽量不要在where条件中使用函数,否则将不能使用索引
    避免使用 select *, 只取需要的字段
    对于大数据量的查询,尽量避免在SQL语句中使用order by 字句,避免额外的开销
    如果插入的数据量很大,用select into 替代 insert into 能带来更好的性能
    采用连接操作,避免过多的子查询,产生的CPU和IO开销
    只关心需要的表和满足条件的数据
    适当使用临时表或表变量
    对于连续的数值,使用between代替in
    where 字句中尽量不要使用CASE条件
    尽量不用触发器,特别是在大数据表上
    更新触发器如果不是所有情况下都需要触发,应根据业务需要加上必要判断条件   
    使用union all 操作代替OR操作,注意此时需要注意一点查询条件可以使用聚集索引,
    如果是非聚集索引将起到相反的结果
    当只要一行数据时使用 LIMIT 1
    尽可能的使用 NOT NULL填充数据库
    拆分大的 DELETE 或 INSERT 语句
    批量提交SQL语句

4、架构优化

1)业务拆分:搜索功能,like ,前后都有%,一般不用MySQL数据库
2)业务拆分:某些应用使用nosql持久化存储,例如memcahcedb、redis、ttserver 比如粉丝关注、好
友关系等;
3)数据库前端必须要加cache,例如memcached,用户登录,商品查询
4)动态数据静态化。整个文件静态化,页面片段静态化
5)数据库集群与读写分离;
6)单表超过2000万,拆库拆表,人工或自动拆分(登录、商品、订单等)

5、流程、制度、安全优化

任何一次人为数据库记录的更新,都要走一个流程
1)人的流程:开发-->核心开发-->运维或DBA
2)测试流程:内网测试-->IDC测试-->线上执行
3)客户端管理:phpmyadmin等

8.7 数据库三范式

为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须满足一定的范式。

在实际开发中最为常见的设计范式有三个:

1.第一范式(确保每列保持原子性)

第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。要求任何一张表必须有主键,每一个字段原子性不可再分。

第一范式的合理遵循需要根据系统的实际需求来定。第一范式是最重要的范式。所有的表的设计都需要满足,必须有主键,并且所有给字段都是原子性不可再分的。

编号姓名联系方式
1001张三zs@126.com 18994868548
1002李四ls@126.com 18995486324

以上设计就是不合适的,不满足第一范式,因为没主键,第二联系方式可以继续拆分

编号PK姓名邮箱地址电话
1001张三zs@126.com18994868548
1002李四ls@126.com18995486324
  1. 第二范式(确保表中的每列都和主键相关)

第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

第二范式,建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖。

学号姓名教师编号教师姓名
1001张三001王老师
1002李四002卢老师

以上设计不满足第一范式,没主键。修改表。将学号+教师编号 PK.

学号教师编号学生姓名教师姓名
1001001张三王老师
1002002李四卢老师
1003001王五王老师

但是不满足第二范式,因为姓名依赖学号。教师姓名依赖教师编号。产生部分依赖造成数据冗余,空间浪费。

需要修改

学生编号学生姓名
1001张三
1002李四
教师编号PK教师姓名
001王老师
002卢老师
PK学生编号FK教师编号FK
11001001
21002002

这样设计,在很大程度上减小了数据库的冗余。

3.第三范式

第三范式:建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖。

学生编号PK姓名班级编号班级名称
1001张三001一年级一班
1002李四002一年级二班
1003王五001一年级一班

满足第一范式?满足,有主键

满足第二范式?满足,因为主键不是复合主键,没有产生部分依赖,主键是单一主键。

满足第三范式?不满足,第三范式要是不产生传递依赖。一年级一班依赖001,001依赖1001 不符合。

以上拆分两张表即一个学生表,一个班级表,在学生表设计外键即可。

面向对象的思想去分析

分析业务涉及的实体

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WHearTBeat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值