基本概念
ACID
事务处理系统必须具备四种特性,既原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability)
原子性:
一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性
一致性:
数据库总是从一个一致性的状态转换到另外一个一致性的状态,既,在执行完一部分事务后系统中断,前边所执行的语句也会回滚
隔离性:
通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的,在前面的例子中,当执行完扣除账户余额的语句时,此时有另外一个账户所看到的余额并没有被减去,另见“隔离级别”。
持久性:
一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。
隔离级别
READ UNCOMMITTED (未提交读)
事务中执行过的语句,产生的结果影响,未提交,对其他事务也都是可见的。事务可以读取未提交的数据,为脏读。
READ COMMITTED(提交读)
大多数数据库系统默认的隔离级别,但mysql不是,提交读满足隔离性的定义,既一个事务开始时,只能看见已经提交的事务所做的修改,这个级别有时也叫不可重复读,因为,在事务中多次读取同一个数据,可能产生不同的结果。
REPEATABLE READ(可重复读)
该级别保证了在同一个事务中多次读取同样的数据的结果是一致的,但是会导致幻读(幻读指的是,读取范围内的数据,如果两次读取中又插入了一行新的,则也会被读取出来,Innodb通过多版本并发控制MVCC,解决了幻读的问题。以后讨论)
SERIALIZABLE(可串行化)
既指的是将所有的事务串行执行,这样解决了幻读的问题,但是会在每一行读取的数据中都加上锁,所以可能导致大量的超时和锁争用的问题。实际很少使用。
事务日志
使用事务日志帮助提高事务的效率,使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘,磁盘上的事务日志采用的是追加的方式,因此写日志的操作是磁盘上一小块区域内的顺序I/O,而不像随机I/O需要在磁盘的多个地方移动磁头,所以会快很多,事务日志持久以后内存中被修改的数据在后台可以慢慢的刷回到磁盘。修改数据需要写两次磁盘。如果数据的修改已经记录到事务日志并持久化,单数据本身还没有写回磁盘,此时系统崩溃,存储引擎在重启时能够自动回复这部分修改的数据。
在事务中混合使用存储引擎
在事务中混合使用不同的存储引擎,比如Innodb和MyIsam,在正常提交的情况下没什么影响,但是如果需要回滚的话MyIsam就不行了,因为这个存储引擎是永远自动提交的。
参数
自动提交状态(修改此值不会对MyIsam产生影响,一直处于autocommit状态)
show variables like 'AUTOCOMMIT';
设置隔离级别(设置为读提交的隔离级别)
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
显示表的相关信息
SHOW TABLE STATUS LIKE 'user' \G
MMVC多版本并发控制
InnoDB的MMVC,是通过在每行记录后面保存两个隐藏列来实现的,这两个列,一个保存了行的创建时间,一个保存行的过期时间(或者删除时间),实际存储的不是时间,是系统版本号,每开始一个新的事务,系统版本号就会自动递增,事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。下面是在REPEATABLE READ 隔离级别下,MVCC具体是如何操作的。
SELECT
InnoDB 会根据一下两个条件检查每行记录:
a.InnoDB至查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的
b.行的删除版本要么未定义,要么大于当前事务版本号,这可以确保事务读取到的行,在事务开始之前未被删除。
只有符合上述两个条件的记录,才能返回作为查询结果。
INSERT
InnoDB 为新插入的每一行保存当前系统版本号作为行版本号
DELETE
InnoDB 为删除的每一行保存当前系统版本号作为删除标识。
UPDATE
InnoDB 为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识
间隙锁
InnoDB 通过间隙锁(next-key locking)策略防止幻读的出现,间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影行的插入
InnoDB 索引
InnoDB 表是基于聚簇索引建立的,聚簇索引对主键查询有很高的性能,不过它的二级索引(非主键索引)中必须包含主键列,所以如果主键列很大的话,其他的所有索引都会很大,因此,若表上的索引较多的话,主键应当尽可能的小
在线更改表引擎
mysql>CREATE TABLE innodb_table LIKE myisam_table;
mysql>ALTER TABLE innodb_table ENGINE=InnoDB;
mysql>INSERT INTO innodb_table SELECT * FROM myisam_table;
或者分批来做
mysql>START TRANSACTION;
mysql>INSERT INTO innodb_table SELECT * FROM myisam_table WHERE id BETWEEN x AND y;
mysql>COMMIT;
还有,Percona Toolkit 提供了一个pt-online-schema-change 的工具,可以比较简单、方便的执行上述过程
配置参数
确定mysql每个连接使用的内存
sort_buffer_size 有排序操作会分配
join_buffer_size 连接缓冲池大小,每个连接会分配一个大小,所以一个查询中,可能分配多个缓冲池大小
read_buffer_size myisam全表扫描所分配的读缓冲池大小
read_rnd_buffer_size 索引缓存区的大小
如何为缓存池分配内存
Innodb_buffer_pool_size
总内存-(每个线程所需要的内存*最大连接数)-系统保留内存
key_buffer_size
myisam 缓存索引的缓存大小,值为 select sum(index_length) from information.schema.tables where engine='myisam';
I/O相关配置
Innodb_log_file_size 控制单个事务日志的大小
Innodb_log_files_in_group 事务日志的个数
Innodb_log_buffer_size 事务日志写入到磁盘持久化之前的缓存池大小 32M左右
Innodb_flush_log_at_trx_commit
0 每秒进行一次log写入cache,并flush log 到磁盘
1 默认,在每次事务提交执行log写入cache,并flush log 到磁盘,最安全的设置
2 建议,每次事务提交执行log写入到cache,每秒中flush log到磁盘,这样的设置,如果mysql进程崩溃,数据不会丢失,除非机器整个宕机,才会丢失最多1秒的数据
Innodb_flush_method=O_DIRECT
数据文件的写入操作是直接从mysql innodb buffer到磁盘的,并不用通过操作系统的缓冲,而真正的完成也是在flush这步,避免了存储引擎和操作系统的双重缓存
Innodb_file_per_table=1
Innodb会为每个表建立一个单独的表空间
Innodb_doublewrite=1
Innodb 双写缓存(查一下),增加安全性
delay_key_write
OFF:每次写操作后刷新键缓冲中的脏块到磁盘
ON:只对在建表时指定了delay_key_write选项的表使用延迟刷新
ALL:对所有MYISAM表都使用延迟键写入
如果设置了OFF以外的选项,当系统崩溃以后,可能就需要修复表(REPAIR TABLE `table_name`)
安全相关配置参数
expire_logs_days 指定自动清理binlog的天数
至少覆盖两次全备所间隔的天数,如果每天全备,至少保存7天左右
max_allowed_packet 控制Mysql可以接受的包的大小
如果有主从复制,则应该把主从这个配置项设置为一样的,避免主从同步失败
skip_name_resolve 禁用DNS查找
sysdate_is_now 确保sysdate()返回确定性日期
read_only 禁止非super权限的用户写权限,保证主从一致,用于slave
skip_slave_start 禁用slave自动恢复,mysql崩溃后重启会自动启动start slave,应该设置为禁止
sql_mode
strict_trans_tables、strict_all_tables
严格模式控制mysql如何处理非法(数据类型错误,不适合列,超出范围)或者丢失的输入值(空,且没有default值)
对于事务表,当启用STRICT_ALL_TABLES或者STRICT_TRANS_TABLES模式时,如果有非法或者丢失,则会出现错误,语句被放弃并滚动。
对于非事务表。建议事务不要使用其他非事务表
no_auth_create_user
在使用grant 给新用户授权后,未加identified by 创建一个密码,则会导致穿件 一个无密码的sql用户。使用此模式后,此情况避免。
no_engine_subtitution
no_zero_date
no_zero_in_date
only_full_group_by
其他常用配置
sync_binlog 控制mysql如何向磁盘刷新binlog
0 表示mysql不会主动刷新,让操作系统决定
n 表示两次向磁盘刷新binlog间隔多少个事务,应该在主从的主服务器设置为1
tmp_table_size 和 max_heap_table_size
控制内存临时表大小,应该保持一致,如果是隐式临时表,超过了设置的值,那么会被转换为磁盘临时表
max_connections 控制允许的最大连接数
重做日志与二进制日志的区别
1、记录的范围不同:二进制日志会记录MySQL的所有存储引擎的日志记录(包括InnoDB、MyISAM等),
而InnoDB存储引擎的重做日志只会记录其本身的事务日志。
2、记录的内容不同:二进制日志文件记录的格式可以为STATEMENT或者ROW也可以是MIXED,其记录的都是关于一个事务的具体操作内容。InnoDB存储引擎的重做日志文件记录的关于每个页的更改的物理情况。
3、写入的时间也不同:二进制日志文件是在事务提交前进行记录的,而在事务进行的过程中,不断有重做日志条目被写入到重做日志文件中。
基于行和基于语句复制的优缺点
基于SQL语句的复制
缺点
对于非确定性事件,无法保证主从服务器的一致性,比如uuid函数、存储过程,触发器,自定义函数等修改可能造成数据不一致
相比于基于行的复制方式在从上执行需要更多的行锁
优点
基于语句的复制产生的网络I/O比较少
并不强制要求主从数据库的表定义完全相同
对于大表的表结构修改,然后再进行主从服务器切换,减少对大表修改时,所需要停机时间
基于ROW语句的复制(推荐,对主从数据一致性有更好的保证)
优点
可以应用于任何sql的复制包括非确定性函数存储过程等
可以减少锁的使用
缺点
要求主从数据库的表结构相同,否则可能会中断复制
无法在从上单独执行触发器
复制性能优化
1、由于事务在主服务器上执行完以后才会通过网络发送给从服务器,所以,尽量减少事务大小是一个不错的优化方法
2、二进制日志格式使用MIXED,或者设置binlog_row_image=minimal
3、使用多线程复制
mysql5.6只能针对所有数据库来进行多线程复制,如果只有一个数据库,或者写密集只在一个数据库中,多线程复制效果不好。
mysql5.7新增了逻辑时钟(logic_clock)多线程复制
stop slave;
set global slave_parallel_type='logical_clock';
set global slave_parallel_workers=4
start slave;
主从复制常见问题处理
1、由于数据损坏或丢失所引起的主从复制错误
主库或者从库以外宕机引起的错误
主库上的二进制日志文件损坏
2、从库上进行数据修改造成的主从复制错误
3、max_allow_packet设置引起的主从复制错误
query_cache参数详解(show global status like '%Qcache%')
Qcache_free_blocks:目前还处于空闲状态的 Query Cache 中内存 Block 数目
Qcache_free_memory:目前还处于空闲状态的 Query Cache 内存总量
Qcache_hits:Query Cache 命中次数
Qcache_inserts:向 Query Cache 中插入新的 Query Cache 的次数,也就是没有命中的次数
Qcache_lowmem_prunes:当 Query Cache 内存容量不够,需要从中删除老的 Query Cache 以给新的 Cache 对象使用的次数
Qcache_not_cached:没有被 Cache 的 SQL 数,包括无法被 Cache 的 SQL 以及由于 query_cache_type 设置的不会被 Cache 的 SQL
Qcache_queries_in_cache:目前在 Query Cache 中的 SQL 数量
Qcache_total_blocks:Query Cache 中总的 Block 数量
可以根据这几个状态计算出 Cache 命中率,计算出 Query Cache 大小设置是否足够,总的来说,我个人不建议将 Query Cache 的大小设置超过256MB,这也是业界比较常用的做法。
mysql 命令技巧
如何不在命令行添加密码而登录上mysql服务器
mysql --defaults-extra-file=/opt/scripts/my.cnf -e 'show databases'
--defaults-extra-file=/file/to/path 指定此参数,文件里写上[client]块,内容如下
[client]
port = 3306
socket = /tmp/mysql.sock
user = root
host = localhost
password = mysql*()
如何在命令行得到不带边框的结果
-H, --html Produce HTML output.
-X, --xml Produce XML output
-r, --raw Write fields without conversion. Used with --batch.
--reconnect Reconnect if the connection is lost. Disable with --disable-reconnect. This option is enabled by default.
-s, --silent Be more silent. Print results with a tab as separator,each row on new line.
1,去除边框
mysql -s -r 就不显示原来的边框,方便下一步处理结果
2,html table方式显示结果
mysql -H 这个方便做些监控结果使用,邮件直接查看
3,xml结果
mysql -X 哈哈,这个可以直接做个数据接口使用