MySQL优化--MySQL属性存储类型与事务机制隔离基础讲解篇
之前通过视频网站买了mysql优化技能,学习完毕以后收获颇丰,才明白之前用的MySQL都是瞎用,只有了解之后你才能知道什么存储引擎适合什么系统,什么情况下应该加索引什么情况下不加索引,索引用什么sql语句会失效,用什么sql语句会有效,庞大的数据表或者是高并发压力是用分库分表还是读写分离等等等等,反正对数据库是有新的认识了!今天我就来总结第一篇MySQL基础知识。
1.影响MySQL性能的主要原因
- 数据库结构设计
- 数据库存储引擎
- SQL语句的书写
- 数据库参数配置
- 服务器操作系统与硬件
1.数据库设计就是建表时的类型的选择,MySQL的数据类型支持的非常多,选择正确的数据类型对于获得高性能至关重要。一般情况下,应该尽量使用可以正确存储的最小数据类型。更小的数据类型通常更快,因为他们占用更少的磁盘、内存、CPU和缓存,并且处理时需要的CPU周期也更少
2.数据库存储引擎有多种,举例 MyISAM:不支持事务,表级锁。InnoDB:事务级存储引擎,完美支持行级锁,事务ACID特性;ps表级锁锁住一整张表,行级锁只锁住某一列,那么正常情况下使用表还是行级锁性能要高啊!
3.SQL语句的编写能大大提高性能
4.数据库参数配置 ,数据库参数也很重要比如说设置最大的连接数啊,缓存数据啊等等
5.服务器操作系统与硬件,这个就不用解释了服务器硬件好那程序应用也高
2.数据库设计
2.1 第一范式
- 数据库所有字段都只有单一属性
- 单一属性是由基本数据类型构成的
- 数据库的表都是二维的(行与列)
2.2 第二范式
- 要符合第一范式
- 表必须有一个主键(一列或多列)
- 其他字段可由主键确定
- 二范式目的是通过拆表来减少数据冗余
2.3 第三范式
- 要求符合第二范式
- 字段要求直接依赖于主键,不允许间接依赖
- 第三范式目的在于拆分实体(完善主从表)
3.实体关系分析
3.1 实体关系分析
- 实体关系是指系统事物之间的联系
- 实体关系需要双向分析
- 实体关系决定表关系
3.2 实体关系的种类
- 一对一
- 一对多
- 多对多
3.3 表关系设计原则
- 一对一,通过主键关联
- 一对多,在多的地方设置外键
- 多对多,增加中间表,持有双方外键
4.自然主键与代理主键
- 自然主键是指事物属性中的自然唯一标示
- 代理主键是指与业务无关的,无意义的数字序值(数据库自增)
- 在表设设计时,优先推荐代理主键,不推荐自然主键
5.数据类型的选择
5.1 字段类型的优先级
- 数字类型(计算机最好识别的语言)
- 日期类型与二进制类型
- 字符串类型
5.2 整型类型
mySql分为有符号(SIGNED)和无符号(UNSIGNED),如果int值不会出现负数的话,可以选择无符号,无符号代表数字可以取值是0和正数,有符号会可以是负数。
一般情况下表的自增id都要设置成bigint,因为表的数据无限增长那么设置最大的范围比较合适,如果有表设计字段比如是is_Del是否删除0是未删除、1是已删除,像这样的就应该使用thinyint存储空间小取值范围也小就很适合
下图是整数类型信息和取值范围
5.3.实数类型
因为需要额外的空间和计算开销,所以应尽量只在对小数进行精确计算时才使用DECIMAL —— 例如财务数据。但在数据量比较大的时候,可以考虑使用BIGINT 代替 DECIMAL,将需要存储的货币单位根据小数的位数乘以相应的倍数即可,这样可以避免浮点存储计算不精确和DECIMAL 精确计算代价高的问题。
5.4.varchar类型
varchar 变体长度,根据实际内容保存数据
- 使用最小的符合需求的长度
- Varchar(255) 以下需要再使用额外的一个字节来保存长度
- Varchar(255) 以上需要再使用额外的两个字节来保存长度
- Varchar(5)和varchar(200) 内存占用不同,varchar在内存中是定长的,前者在内存中只开辟了5个内存,后者需要开辟200的内存
- Varchar变更长度时会出现锁表 ,虽然mysql6版本已经有了改善,但是Varchar(255) 变更可能还是会出现问题
5.4.1.varchar使用场景
- Varchar适合存储长度波动大的数据
- 字符串很少被更新的场景
- Varchar适合保存多字节字符
5.5.char类型
char属于定长数据,char定义好以后就不会发生长度改变
- char的最大长度是255
- char类型会自动删除末尾的空格
- char的检索效率比varchar高,char是以空间换时间,设置好的空间不会变,所以就在那段空间去搜索,效率要比varchar高,写也要比varchar快
5.5.1.char的应用场景
- chat适合存储波动不大的数据,如MD5多少位的密码等等
- char适合存储短字符串
- char类型适合存储经常更新的字符串
5.6.DATETIME日期类型
- DATETIME日期时间类型,占用8个字节
- DATETIME与时区无关,可保存到毫秒,可保存时间范围大
- 不要使用字符串存储日期类型,因为这样自带的处理时间函数可能使用不了,字符串也没有DATETIME效率高
5.7 TIMESTIMP时间戳
- Timestimp时间戳,占用4个字节,时间范围精确到1970-01-01(格林尼治)到2038-01-19,精确到秒,采用整型存储,依赖于时区,自动更新Timestimp列的值。可以把创建时间改成Timestimp方式,这样就不用额外的去存储,当存储时直接就有一条时间。
若定义一个字段为timestamp,这个字段里的时间数据会随其他字段修改的时候自动刷新,所以这个数据类型的字段可以存放这条记录最后被修改的时间。
6.反范式设计
反范式设计就是为了让sql更加的简便而出现的,故意的增加表冗余,让sql语句变的更简单
6.1.反范式优缺点
- SQL语句简单,有利于程序开发,团队协作
- 单表查询易于优化,易于管理
- 存在数据冗余,写操作时需要额外更新从表数据
- 不合理的反范式设计会让表变得臃肿不堪
7.事务的特性
7.1.原子性
事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。
如:张三给李四转账,必须是张三扣钱、李四加钱这两个环节一起完成,这两个动作必须是原子化的,要么都成功,要么都失败
7.2.一致性
事务开始前和结束后,数据库的完整性约束没有被破坏。
如:上面转账的例子,双方必须保持一致,不能说张三的钱被扣了,但是李四的钱没有加上,这样就会导致数据错乱
7.3.隔离性
同一时间,只允许一个事务请求同一个数据,不同的事物之间彼此没有任何干扰
如:在张三转账这个事务没有完成之前,其他事务看到张三的钱不会变少,假设开始有100元,那么在他没完成转账之前,看到的还应该是100元
7.4.持久性
事物完成后,事物对数据库的所有更新将被保存到数据库,不能回滚。
如:转账完成之后,数据就会固定下来,不可回滚,此时其他事务可以正确看到张三扣钱之后的金额
8.事务的隔离性
8.1.Read uncommitted(读未提交)
一个事物可以读取另一个未提交事物的数据。由于只要库里数据改变没有提交都会被读取,所以就会出现脏读。甚至不可重复读,幻读,所以很少使用此。
事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。
8.2.Read committed(读已提交)
一个事务要等另一个事物提交后才能读取数据。在读的时候,若有事务对数据进行更新(update)操作时,读操作事务要等待这个更新操作事物提交之后才能读取事务可以解决脏读问题,但是以下例子中出现了一个事务范围内两个相同的查询却返回了不同的数据,这个是不可重复读。
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他买单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
8.3.Repeatable read(重复读)
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作。
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他买单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
分析:重复读可以解决不可重复读,不可重复读对应的是修改,即UPDATE操作。但是还有可能出现幻读问题,幻读问题对应的是插入insert操作,而不是UPDATE操作。
8.4.什么时候会出现幻读?
事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。
8.5.Serializable(序列化)
Serializable是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读、幻读,但是这种事务级别效率低下,比较耗数据库性能,一般不使用。Serializable(序列化)不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了;
MySQL基本知识就记录到这里。