1. mysql基础
1.MySQL的默认端⼝号是3306。
1.1 InnoDB和MyISAM存储引擎
MyISAM和InnoDB区别
主要有5点,关键字为:1.事务 2.外键 3.聚簇索引 4.行级锁 5. MVCC(多版本并发控制)
- InnoDB 支持事务,MyISAM 不支持事务。
- InnoDB 支持外键,而 MyISAM 不支持。
- InnoDB 是聚集索引,MyISAM 是非聚集索引。聚簇索引的文件存放在主键索引的叶子节点上,因此 InnoDB 必须要有主键,通过主键索引效率很高。而 MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
- InnoDB ⽀持⾏级锁(row�level locking)和表级锁,默认为⾏级锁,MyISAM 只有表级锁(table-level locking).
- InnoDB 支持MVCC(多版本并发控制):仅InnoDB 支持。应对高并发事务,MVCC 比单纯的加锁更加高效;MVCC 只在 EAD COMMITTED(读已提交) 和 REPEATABLE READ(可重复读) 两个级别下工作;MVCC 可以使用乐观锁(optimistic)和 悲观锁(pessimistic)来实现;各数据库中实现MVCC的方式并不统一。
两者如何选择?
⼤多数时候我们使⽤的都是 InnoDB 存储引擎,但是在某些情况下使⽤ MyISAM 也是合适的⽐如读密集的情况下。(如果你不介意 MyISAM 崩溃恢复问题的话)。
1.2 索引
主要有两种索引,B+Tree索引和哈希索引,对于哈希索引来说,底层的数据结构就是哈希表,因此在绝⼤多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余⼤部分场景,建议选择BTree索引。
在 MyISAM引擎下,B+Tree索引为非聚簇索引,节点保存了数据。而在InnoDB引擎,索引⽂件和数据⽂件是分离的,在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再⾛⼀遍主索引。 因此,在设计表的时候,不建议使⽤过⻓的字段作为主键,也不建议使⽤⾮单调的字段作为主键,这样会造成主索引频繁分裂
1.3 数据库缓存
开启查询缓存后在同样的查询条件以及数据情况下,会直接在缓存中返回结果。缓存虽然能够提升数据库的查询性能,但是缓存同时也带来了额外的开销,每次查询后都要做⼀次缓存操作,失效后还要销毁。
缓存虽然能够提升数据库的查询性能,但是缓存同时也带来了额外的开销,每次查询后都要做⼀次缓存操作,失效后还要销毁。
还可以通过sql_cache和sql_no_cache来控制某个查询语句是否需要缓存。
1.4 事务
事务是逻辑上的⼀组操作,要么都执行,要么都不执行。
事务的4个特性:
- 原⼦性(Atomicity): 事务是最⼩的执⾏单位,不允许分割。事务的原⼦性确保动作要么全部完成,要么完全不起作⽤;
- ⼀致性(Consistency): 执⾏事务前后,数据保持⼀致,多个事务对同⼀个数据读取的结果是相同的;
- 隔离性(Isolation): 并发访问数据库时,⼀个⽤户的事务不被其他事务所⼲扰,各并发事务之间数据库是独⽴的;
- 持久性(Durability): ⼀个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发⽣故障也不应该对其有任何影响。
1.5 并发事务带来哪些问题
1.5.1 并发事务带来哪些问题
- 脏读(Dirty read):B事务读到了A事务已修改但未提交的数据。
- 丢失修改(Lost to modify):这样第⼀个事务内的修改结果就被丢失,因此称为丢失修改。
- 不可重复读(Unrepeatableread): 指在⼀个事务内多次读同⼀数据,由于第⼆个事务的修改导致第⼀个事务两次读取的数据可能不太⼀样。
- 幻读(Phantom read):幻读与不可重复读类似。它发⽣在⼀个事务(T1)读取了⼏⾏数据,接着另⼀个并发事务(T2)插⼊了⼀些数据时。在随后的查询中,第⼀个事务(T1)就会发现多了⼀些原本不存在的记录,就好像发⽣了幻觉⼀样,所以称为幻读。
1.5.2 事务隔离级别有哪些?MySQL的默认隔离级别是?
SQL 标准定义了四个隔离级别:
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻⽌脏读,但是幻读或不可重复读仍有可能发⽣。
REPEATABLE-READ(可重复读): 对同⼀字段的多次读取结果都是⼀致的,除⾮数据是被本身事务⾃⼰所修改,可以阻⽌脏读和不可重复读,但幻读仍有可能发⽣。
SERIALIZABLE(可串⾏化): 最⾼的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执⾏,这样事务之间就完全不可能产⽣⼲扰,也就是说,该级别可以防⽌脏读、不可重复读以及幻读。
1.5.3 锁机制与InnoDB锁算法
MyISAM和InnoDB存储引擎使⽤的锁:
- MyISAM采⽤表级锁(table-level locking)。
- InnoDB⽀持⾏级锁(row-level locking)和表级锁,默认为⾏级锁
表级锁和行级锁可以进一步划分为共享锁(s)(读锁)和排他锁(X)(写锁)
InnoDB存储引擎的锁的算法有三种:
Record lock:单个⾏记录上的锁
Gap lock:间隙锁,锁定⼀个范围,不包括记录本身
Next-key lock:record+gap 锁定⼀个范围,包含记录本身
1.6 大表优化
- 限定数据的范围。务必禁⽌不带任何限制数据范围条件的查询语句。
- 读/写分离。经典的数据库拆分⽅案,主库负责写,从库负责读;
- 垂直分区。根据数据库⾥⾯数据表的相关性进⾏拆分。简单来说垂直拆分是指数据表列的拆分,把⼀张列⽐较多的表拆分为多张表。
垂直拆分的优点: 可以使得列数据变⼩,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。
垂直拆分的缺点*: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应⽤层进⾏Join来解决。此外,垂直分区会让事务变得更加复杂;
-
水平拆分。保持数据表结构不变,通过某种策略存储数据分⽚。这样每⼀⽚数据分散到不同的表或者库中,达到了分布式的⽬的。 ⽔平拆分可以⽀撑⾮常⼤的数据量。
水平拆分最好分库 。 ⽔平拆分能够⽀持⾮常⼤的数据量存储,应⽤端改造也少,但 分⽚事务难以解决 ,跨节点Join性能差,逻辑复杂。《Java⼯程师修炼之道》的作者推荐 尽量不要对数据进⾏分⽚,因为拆分会带来逻辑、部署、运维的各种复杂度 ,⼀般的数据表在优化得当的情况下⽀撑千万以下的数据量是没有太⼤问题的。如果实在要分⽚,尽量选择客户端分⽚架构,这样可以减少⼀次和中间件的⽹络I/O。
1.6.1 分库分表之后,id 主键如何处理?
因为要是分成多个表之后,每个表都是从 1 开始累加,这样是不对的,我们需要⼀个全局唯⼀的id 来⽀持。
⽣成全局 id 有下⾯这⼏种⽅式:
- UUID:不适合作为主键,因为太⻓了,并且⽆序不可读,查询效率低。⽐适合⽤于⽣成唯⼀的名字的标示⽐如⽂件的名字。
- **数据库⾃增 id **: 两台数据库分别设置不同步⻓,⽣成不重复ID的策略来实现⾼可⽤。这种⽅式⽣成的 id 有序,但是需要独⽴部署数据库实例,成本⾼,还会有性能瓶颈。
- 利⽤ redis ⽣成 id : 性能⽐好,灵活⽅便,不依赖于数据库。但是,引⼊了新的组件造成系统更加复杂,可⽤性降低,编码更加复杂,增加了系统成本。
- Twitter的snowflake算法 :Github 地址:https://github.com/twitter-archive/snowflake。
- 美团的Leaf分布式ID⽣成系统 :Leaf 是美团开源的分布式ID⽣成器,能保证全局唯⼀性、趋势递增、单调递增、信息安全,⾥⾯也提到了⼏种分布式⽅案的对⽐,但也需要依赖关系数据库、Zookeeper等中间件。感觉还不错。美团技术团队的⼀篇⽂章:https://tech.meituan.com/2017/04/21/mt-leaf.html