一、事务
概念
事务指的是满足 ACID 特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚。
ACID
- 原子性:一个事务看成一个整体,要么成功提交,要么全部回滚。
- 一致性:事务操作前后,对于数据库的数据时保持一致的。
- 隔离性:一个事务在最终提交之前,对于其他事务没有影响。
- 持久性:一旦事务发生,对于数据库的修改就是永久的。就算数据库崩溃,修改也依然存在。
事务的 ACID 特性概念简单,但不是很好理解,主要是因为这几个特性不是一种平级关系:
- 只有满足一致性,事务的执行结果才是正确的。
- 在无并发的情况下,事务串行执行,隔离性一定能够满足。此时只要能满足原子性,就一定能满足一致性。
- 在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。
- 事务满足持久化是为了能应对数据库崩溃的情况。
Mysql的自动提交
在mysql中,针对每一个查询,如果没有提交输入 start transaction 开启一个事务,那么默认每一个查询就是一个事务,会直接提交。
二、并发一致性问题
数据库的并发一致性问题主要针对,在并发的情况下,隔离性很难保证,出现一系列问题。
- 丢失修改:T1先修改了记录1还未提交,T2又修改了记录1并提交。此时T2的修改覆盖了T1的修改
- 脏读:T1先读数据,此时T1对数据做修改,T1读到修改的数据,然后T2回滚了,此时T1读到的就是T2为提交的数据为脏读。
- 不可重复读:T1先读数据为X,然后T2修改了X ,T1又去读,读到不同的X。即在同一次查询中两次读到的结果不同。
- 幻读:不可重复读针对修改操作。幻读针对插入操作,即在同一次查询中,两次查的结果不同,在两次查询之间有人插入新的数据导致查询结果不同。
产生并发不一致性问题主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。并发控制可以通过封锁来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的隔离级别,让用户以一种更轻松的方式处理并发一致性问题。
三、封锁
封锁粒度
数据库提供两种级别的锁:表锁和行级锁。
应当根据数据的量大小选择锁的级别,并不是锁定所有资源。锁的数据量越少,发生争抢的可能性就越小,并发性能越高。
但是锁操作会带给系统开销,(获取锁,释放锁),锁的粒度越小,锁的数量越多,带来的系统开销越大。应当在并发性能和系统开销之间进行权衡。
封锁类型
共享锁(Shared):又称为读锁。
排它锁(Exclusive):又称为写锁。
规定:
- 当事务一条数据被加上共享锁,表示可以对其实现读操作,但不能更新操作,允许其他事务对其加共享锁,但不能加排它锁(写锁)。
- 当一条数据被加上排它锁,可以对其读和修改,但是其他事务不能对其加其他的锁(共享锁和排它锁)。
锁的兼容关系如下:
- | X | S |
---|---|---|
X | × | × |
S | × | √ |
意向锁:
为了保证多种粒度的锁的共存。--使用意向锁为了实现多级粒度封锁。
意向锁都为表级锁。
当表级锁和行级锁共存的情况下,如果事务相对A或A表中一行加锁,那么就要针对A表和A表中每一行数据进行检索,是否有加锁,这样耗费巨大。
意向锁就是在X/S的基础上,引入IX/IS锁,用来表示若事务想要在表中某行数据加锁,那么就有以下规定:
- 一个事务想要在数据上加S锁,那么就要获得IS锁,或者更强的锁。
- 一个事务想要在数据上加X锁,那么必须获得IX锁。
通过意向锁,如果事务想对A加X锁,那么就要检查是否有其他事务加IS/S/IX/X锁,若有就不能加锁。
各种锁的兼容关系如下:
- | X | IX | S | IS |
---|---|---|---|---|
X | × | × | × | × |
IX | × | √ | × | √ |
S | × | × | √ | √ |
IS | × | √ | √ | √ |
解释如下:
- 但是本身IS/IX是不冲突,因为表示意向,指想要对其加锁。
- S 锁只与 S 锁和 IS 锁兼容,也就是说事务 T 想要对数据行加 S 锁,其它事务可以已经获得对表或者表中的行的 S 锁
Mysql中加锁语句:
MyISAM支持表级锁,InnoDB不仅支持事务,还支持行级锁。
意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB可以使用for update 操作会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会任何锁;事务可以通过以下语句显示给记录集加共享锁或排锁。
共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。
排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE。
总结如下:
- 意向锁:Innodb引擎自动加锁,且表级锁,不用用户自己的干预。
- 排它锁:针对Innodb默认的修改操作,insert,delete,update数据库会默认给其对应数据集加上排它锁X。
- select操作:默认什么锁都不加。若想要加排他锁:select.....for update . 若想要加 共享锁,select ...Lock in share mod;
四、隔离级别
四种隔离级别:
- 未提交读:指的是一个事务可以读到另一个事务未提交的数据。
- 提交度:一个事务必须等另一个事务提交才能读取其数据。
- 可重复读:一个事务执行的时候其他事务不可以修改对应数据的内容。表示多次读的结果是一样的。
- 串行化:将所有事务都串行执行。
隔离级别 | 脏读 | 不可重复读 | 幻影读 |
---|---|---|---|
未提交读 | √ | √ | √ |
提交读 | × | √ | √ |
可重复读 | × | × | √ |
可串行化 | × | × | × |
- 未提交度可能会导致脏读。
- 提交度避免脏读,但是会出现不可重复读。
- 可重复读避免不可重复读的情况,但是针对插入修改,无法避免幻读。
五、多版本并发控制
Muti-version concurreny control 是一种数据库实现隔离级别的具体的实现方式。单纯依靠表级锁,行级锁进行并发控制,效率是低下的,所以Innodb实现了MVCC结合行,表锁进行并发控制,提高了并发效率。并发能力强,能处理更多的并发问题。
参考:https://www.jianshu.com/p/a3d49f7507ff
MVCC优缺点
MVCC在大多数情况下代替了行锁,实现了对读的非阻塞,读不加锁,读写不冲突。缺点是每行记录都需要额外的存储空间,需要做更多的行维护和检查工作。
好处:读时不加锁,读写不冲突。
一般用来实现:可重复读和读提交两种隔离级别。
版本号
- 系统版本号:是一个递增的数,每开启一个事务,系统版本号加一。
- 事务版本号:记录事务开启时的系统版本号。
隐藏的列
MVCC在每行数据后,利用隐藏的两列数据保存两个版本号:
- 创建版本号:创建该数据行时的系统版本号
- 删除版本号:删除该数据行的系统版本号,此时删除版本号一定要大于当前事务快照的版本好,若低于当前事务版本号,该数据行可能已被删除。
InnoDB中的实现:
undo log
为了便于理解MVCC的实现原理,这里简单介绍一下undo log的工作过程
在不考虑redo log 的情况下利用undo log工作的简化过程为:
序号 | 动作 |
---|---|
1 | 开始事务 |
2 | 记录数据行数据快照到undo log |
3 | 更新数据 |
4 | 将undo log写到磁盘 |
5 | 将数据写到磁盘 |
6 | 提交事务 |
1)为了保证数据的持久性数据要在事务提交之前持久化
2)undo log的持久化必须在在数据持久化之前,这样才能保证系统崩溃时,可以用undo log来回滚事务
Innodb中的隐藏列
Innodb通过undo log保存了已更改行的旧版本的信息的快照。
InnoDB的内部实现中为每一行数据增加了三个隐藏列用于实现MVCC。
列名 | 长度(字节) | 作用 |
---|---|---|
DB_TRX_ID | 6 | 插入或更新行的最后一个事务的事务标识符。(删除视为更新,将其标记为已删除) |
DB_ROLL_PTR | 7 | 写入回滚段的撤消日志记录(若行已更新,则撤消日志记录包含在更新行之前重建行内容所需的信息) |
DB_ROW_ID | 6 | 行标识(隐藏单调自增id) |
- 一个事务版本号用于记录当前行事务操作 新增还是删除(更新标记为删除)。
- 一个回滚指针,记录undo log必要的信息。
- 一个行标识,隐藏单调自增id。
MVCC工作过程
MVCC只在READ COMMITED 和 REPEATABLE READ 两个隔离级别下工作。READ UNCOMMITTED总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE 则会对所有读取的行都加锁
-
Select
- 需要保证当前事务开启的版本号大于等于当前数据行的系统版本号,即确保事务开启时该数据行已经存在或被当前事务修改。
- 需要保证数据行删除版本号大于当前事务开启的版本号,确保事务开启时当前数据行未被删除。或者删除版本号不存在。
-
Insert
- 为每个新增行增加当前系统版本号作为其行版本号
-
Delete
- 将当前系统版本号作为其删除版本号
-
Update
- 先新插入一行,将当前系统版本号作为新行的版本号,然后将当前版本号作为旧版本数据行的删除版本号。
快照读与当前读
1. 快照读
使用 MVCC 读取的是快照中的数据,这样可以减少加锁所带来的开销。
select * from table ...;
2. 当前读
读取的是最新的数据,需要加锁。以下第一个语句需要加 S 锁,其它都需要加 X 锁。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert;
update;
delete;
六、Next-Key Locks
Next-key Locks 是一种Mysql下InnoDB引擎提供的锁机制。
目的是为了结合MVCC来解决幻读。
-
Record Lock
- 记录锁,锁定的不是记录本身,而是锁定该记录对应的索引。
- 若记录本身没有索引,那么会根据主键创建隐藏的聚集索引进行锁定。因此没有索引也可以使用Record Lock
-
Gap Lock
- 间隙锁,锁定的是记录之间的范围,锁定的也是索引之间的范围,但是不锁定记录索引本身(左开右开区间)。
- 例如:开启一个事务进行查询
-
SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
其它事务就不能在 t.c 中插入 15。
-
Next-key Lock
-
结合了Record Lock 和Gap Lock 。不仅仅在记录索引本身上加锁,并且锁定索引之间的范围。
七、关系数据库设计理论
函数依赖
记 A->B 表示 A 函数决定 B,也可以说 B 函数依赖于 A。
如果 {A1,A2,... ,An} 是关系的一个或多个属性的集合,该集合函数决定了关系的其它所有属性并且是最小的,那么该集合就称为键码。
对于 A->B,如果能找到 A 的真子集 A',使得 A'-> B,那么 A->B 就是部分函数依赖,否则就是完全函数依赖。
对于 A->B,B->C,则 A->C 是一个传递函数依赖。
异常
以下的学生课程关系的函数依赖为 Sno, Cname -> Sname, Sdept, Mname, Grade,键码为 {Sno, Cname}。也就是说,确定学生和课程之后,就能确定其它信息。
Sno | Sname | Sdept | Mname | Cname | Grade |
---|---|---|---|---|---|
1 | 学生-1 | 学院-1 | 院长-1 | 课程-1 | 90 |
2 | 学生-2 | 学院-2 | 院长-2 | 课程-2 | 80 |
2 | 学生-2 | 学院-2 | 院长-2 | 课程-1 | 100 |
3 | 学生-3 | 学院-2 | 院长-2 | 课程-2 | 95 |
不符合范式的关系,会产生很多异常,主要有以下四种异常:
- 冗余数据:例如
学生-2
出现了两次。 - 修改异常:修改了一个记录中的信息,但是另一个记录中相同的信息却没有被修改。
- 删除异常:删除一个信息,那么也会丢失其它信息。例如删除了
课程-1
需要删除第一行和第三行,那么学生-1
的信息就会丢失。 - 插入异常:例如想要插入一个学生的信息,如果这个学生还没选课,那么就无法插入。
范式
范式理论是为了解决以上提到四种异常。
高级别范式的依赖于低级别的范式,1NF 是最低级别的范式。
1. 第一范式 (1NF)
属性不可分。
2. 第二范式 (2NF)
每个非主属性完全函数依赖于键码。
可以通过分解来满足。
分解前
Sno | Sname | Sdept | Mname | Cname | Grade |
---|---|---|---|---|---|
1 | 学生-1 | 学院-1 | 院长-1 | 课程-1 | 90 |
2 | 学生-2 | 学院-2 | 院长-2 | 课程-2 | 80 |
2 | 学生-2 | 学院-2 | 院长-2 | 课程-1 | 100 |
3 | 学生-3 | 学院-2 | 院长-2 | 课程-2 | 95 |
以上学生课程关系中,{Sno, Cname} 为键码,有如下函数依赖:
- Sno -> Sname, Sdept
- Sdept -> Mname
- Sno, Cname-> Grade
Grade 完全函数依赖于键码,它没有任何冗余数据,每个学生的每门课都有特定的成绩。
Sname, Sdept 和 Mname 都部分依赖于键码,当一个学生选修了多门课时,这些数据就会出现多次,造成大量冗余数据。
分解后
关系-1
Sno | Sname | Sdept | Mname |
---|---|---|---|
1 | 学生-1 | 学院-1 | 院长-1 |
2 | 学生-2 | 学院-2 | 院长-2 |
3 | 学生-3 | 学院-2 | 院长-2 |
有以下函数依赖:
- Sno -> Sname, Sdept
- Sdept -> Mname
关系-2
Sno | Cname | Grade |
---|---|---|
1 | 课程-1 | 90 |
2 | 课程-2 | 80 |
2 | 课程-1 | 100 |
3 | 课程-2 | 95 |
有以下函数依赖:
- Sno, Cname -> Grade
3. 第三范式 (3NF)
非主属性不传递函数依赖于键码。
上面的 关系-1 中存在以下传递函数依赖:
- Sno -> Sdept -> Mname
可以进行以下分解:
关系-11
Sno | Sname | Sdept |
---|---|---|
1 | 学生-1 | 学院-1 |
2 | 学生-2 | 学院-2 |
3 | 学生-3 | 学院-2 |
关系-12
Sdept | Mname |
---|---|
学院-1 | 院长-1 |
学院-2 | 院长-2 |
Mysql数据库基础知识
一、索引
索引是一种用于数据库中一列或者多列值排序的数据结构。使用数据库索引可以加速数据访问的速度。
索引的优点和缺点:
优点:
- 加快查询的速度
- 建立唯一索引可以保证每一行的唯一性
- 当使用分组和排序的子查询时,显著加速分组和排序的速度
- 可用于多表之间的连接,加速表之间连接
缺点:
- 创建和删除,维护索引需要额外的开销
- 需要额外的物理空间用于存放索引
- 增删改的时候,需要重新维护索引,增大系统的开销
索引的分类
- 聚簇索引:数据的物理排序与索引的顺序是一样的。一般创建主键后,默认主键为聚簇索引,因为表中数据的物理排放顺序只有一种,所以聚簇索引只能有一个,也可以设置非主键的聚簇索引。
- 非聚簇索引:数据的物理排序与索引的顺序无关。可以有多个非聚簇索引。
- 唯一索引:保证一行数据的唯一性的索引,可以有多个唯一索引允许列为空值。
- 主键索引:特殊的唯一索引,表中只有一个,允许为空值。
- 组合索引:使用多个字段创建的索引,查询时只有使用创建索引的第一个字段才能使索引生效。最左前缀原则。
ALTER TABLE `table` ADD INDEX name_city_age (name,city,age);
索引的数据结构
- B+Tree:多数数据库引擎都支持的一种索引结构。适用于范围查找,是一种树结构的查找类型。
BTree索引是最常用的mysql数据库索引算法,因为它不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符,只要它的查询条件是一个不以通配符开头的常量,例如:
select * from user where name like 'jack%';
select * from user where name like 'jac%k%';
如果一通配符开头,或者没有使用常量,则不会使用索引,例如:
select * from user where name like '%jack';
select * from user where name like simply_name;
- hash 索引:适用于精确查找,对等比较。因为其实一次定位数据,不需要想B Tree从根节点查找到子节点,效率上高于Btree.
- Hash索引仅仅能满足“=”,“IN”,“<=>”查询,不能使用范围查询。
- full text:全文索引,主要用来查找文本中的关键字,是一种用于字符串匹配的索引方式,与一般的索引大不相同。
- R-tree:用于查询比较接近的数据。
索引的使用场合和失效
适合使用索引:
- 经常使用需要查询的列
- 用于排序和分组的列
- 用于表连接的列
不适用的场景:
- 很少用到查询的列
- 修改性能远大于查询的列
- 数据值量很少的列(性别)
- 全表扫描快于索引的时候
索引的失效:
- 查询时使用or,并且条件中不是所有列都建立索引
- 使用like查询,并以%开头
- 全文扫描快于索引的时候
- 组合索引查询,查询条件最左匹配原则未匹配到创建索引的第一个字段
- 当数据类型是字符串,查询条件未将字符串用引号括起来
为什么索引不适用二叉树?
因为二叉树的子节点只有两个,当查找的表中值较多的时候,导致二叉树的深度很大,每次查询导致磁盘IO次数过多,(磁盘IO次数等于比较次数),性能急剧降低。
B-Tree参考:http://www.cnblogs.com/dongguacai/p/7239599.html
从二叉树的查找过程了来看,树的高度和磁盘IO的次数都是4,所以最坏的情况下磁盘IO的次数由树的高度来决定。
从前面分析情况来看,减少磁盘IO的次数就必须要压缩树的高度,让瘦高的树尽量变成矮胖的树,所以B-Tree就在这样伟大的时代背景下诞生了。
B-Tree
一种多叉平衡树,一个m阶的B-Tree有如下特点:
- 根节点至少有两个子节点(当根节点不是叶子节点的情况)
- 每个中间节点(非根节点非叶子节点)有k个子节点(m/2<=k<=m)。
- 所有叶子节点都在同一层,叶子节点没有子节点,有关键码。
- 每个节点都有关键码,关键码以升序排列,关键码的个数为k-1个(m/2<=k<=m)。
如下有一个3阶的B树,观察查找元素21的过程:
第一次磁盘IO:
第二次磁盘IO:
这里有一次内存比对:分别跟3与12比对
第三次磁盘IO:
这里有一次内存比对,分别跟14与21比对
从查找过程中发现,B树的比对次数和磁盘IO的次数与二叉树相差不了多少,所以这样看来并没有什么优势。
但是仔细一看会发现,比对是在内存中完成中,不涉及到磁盘IO,耗时可以忽略不计。另外B树种一个节点中可以存放很多的key(个数由树阶决定)。
相同数量的key在B树中生成的节点要远远少于二叉树中的节点,相差的节点数量就等同于磁盘IO的次数。这样到达一定数量后,性能的差异就显现出来了。
B-Tree的查找过程:
首先根据二分查找,找到对应子节点的位置,然后在子节点内部,按照关键码升序继续利用二分查找,找到关键码对应子节点继续查找,直到找到相应的叶子节点。
B-Tree的插入和删除:每次插入和删除之后都要保持多路平衡(即保持B-Tree的规则)。
插入删除操作记录会破坏平衡树的平衡性,因此在插入删除操作之后,需要对树进行一个分裂、合并、旋转等操作来维护平衡性。
B+Tree:
B+树是B-树的变体,也是一种多路搜索树:
1.其定义基本与B-树同,除了:
2.非叶子结点的子树指针与关键字个数相同;
3.非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树
(B-树是开区间);
5.为所有叶子结点增加一个链指针;
6.所有关键字都在叶子结点出现;
如:(M=3)
B+的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在
非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;
B+的特性:
1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好
是有序的;
2.不可能在非叶子结点命中;
3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储
(关键字)数据的数据层;
4.更适合文件索引系统;
B+和B 树之间的区别:
性能上(也即为什么说B+树比B树更适合实际应用中操作系统的文件索引和数据库索引?)
- 不同于B树只适合随机检索,B+树同时支持随机检索和顺序检索;
- B+树的磁盘读写代价更低。B+树的内部结点并没有指向关键字具体信息的指针,其内部结点比B树小,盘块能容纳的结点中关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素。
- B+树的查询效率更加稳定。B树搜索有可能会在非叶子结点结束,越靠近根节点的记录查找时间越短,只要找到关键字即可确定记录的存在,其性能等价于在关键字全集内做一次二分查找。而在B+树中,顺序检索比较明显,随机检索时,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径长度相同,导致每一个关键字的查询效率相当。
- (数据库索引采用B+树的主要原因是,)B-树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。B+树的叶子节点使用指针顺序连接在一起,只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作(或者说效率太低)。
Hash 索引:
哈希索引建立在哈希表的基础上,它对每个值采用精确查找。每一行都需要先计算哈希码,比较好的哈希算法算出比较低的重复的度,这样效率相对高一些。
Mysql数据库引擎:
常用的引擎两种 :MyISAM和InnoDB
InnoDB
是 MySQL 默认的事务型存储引擎,只有在需要它不支持的特性时,才考虑使用其它存储引擎。
实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,通过多版本并发控制(MVCC)+ 间隙锁(Next-Key Locking)防止幻影读。
主索引是聚簇索引,在索引中保存了数据,从而避免直接读取磁盘,因此对查询性能有很大的提升。
内部做了很多优化,包括从磁盘读取数据时采用的可预测性读、能够加快读操作并且自动创建的自适应哈希索引、能够加速插入操作的插入缓冲区等。
支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。
MyISAM
设计简单,数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作,则依然可以使用它。
提供了大量的特性,包括压缩表、空间数据索引等。
不支持事务。
不支持行级锁,只能对整张表加锁,读取时会对需要读到的所有表加共享锁,写入时则对表加排它锁。但在表有读取操作的同时,也可以往表中插入新的记录,这被称为并发插入(CONCURRENT INSERT)。
可以手工或者自动执行检查和修复操作,但是和事务恢复以及崩溃恢复不同,可能导致一些数据丢失,而且修复操作是非常慢的。
如果指定了 DELAY_KEY_WRITE 选项,在每次修改执行完成时,不会立即将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入磁盘。这种方式可以极大的提升写入性能,但是在数据库或者主机崩溃时会造成索引损坏,需要执行修复操作。
比较
- MyIsam不支持事务,InnoDB支持事务,默认隔离级别可重复读。
- MyIsam支持全文索引,InnoDB没有全文索引
- InnoDB主键默认使用聚簇索引,MyIsam采用的是非聚簇索引。
- InnoDB采用行级锁,具有更高的并发性,Myisam表级锁。
- InnoDB支持外键,Myisam不支持。
- 崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。
选择:
- 当需要事务型操作,并且需要并发操作性能要求时,利用InnoDB有更好的性能。
- 当利用高速检索和全文索引时,或小型数据应用需求,利用MyIsam较好。
数据库调优:
https://www.jfox.info/20-tiao-mysql-xing-nen-you-hua-de-zui-jia-jing-yan.html
高并发处理系统的理解---数据一致性(还有一点问题)
- 服务器配置
- 数据库设计以及优化
- 缓存
- 数据一致性处理
服务器配置:
集群的环境,每个主机选择apahe 还是nginx,nignx的并发性好。nginx和apche区别 以及服务器的配置,例如缓存大小等
根据实际情况,可能对于图像比较多的情况,单独配置nginx服务器,作为图像服务器。在实习中使用的是七牛家的云存储单独作为图片存储,将有关车辆的上传图片全部放在上面。
数据库设计以及优化
(1)表的设计:
存储引擎:innodb还是 myisam? innodb支持事务外键,可以在崩溃时恢复(事务中redo日志实现),myisam不支持;存放数据的方式不同:myisam将表的结构frm 数据myd索引myi存在数据库目标中;innodb只在数据库目标文件中存在表的结构;索引采用B+树,myisam索引叶结点保存的是指针,指向数据,innodb存的就是数据;myisam占用空间小,在读的业务比较多的情况下采用myisam比较好。
字段的设置: 尽量使用短的字段,提高效率,建立索引也能减少资源浪费; 整型类型,比字符类型比较快;varchar 和 char不定长(节约空间)和定长(查询快)选用;索引字段:该字段进行不同的比较多,字段值不易过长。
合理选择数据的冗余:可以根据实际情况,不满足三范式:设置冗余字段,可以减少客户的处理,满足三范式,表之间的关系比较清晰,但是因为有外键什么的,多表关联可能性能降低,加大了用户的编程难度。
索引优化:别人整理的很好
(2)分库分表
针对大表,可以根据实际情况垂直分表或者水平分表。
垂直分表:对于大表中的某些字段经常使用,可以分表;
水平分表:例如月份,将不同的月份的数据存在不同的表中。
(3)mysql集群的环境
读写分离:主要针对读操作比较多的情况下。读写分离别人的,很好
目的:给大型网站缓解查询压力
原理:服务器运行amobe服务,可以判断sql是写还是读操作。收到sql语句是写,服务器将写送到master mysql处理,利用mysql proxy机制然后同步到slave mysql;
当服务器是select时,服务器会根据负载均衡挑选出一个slave mysql,将select语句送到并处理。
缓存:
可以将一些不动的页面:页面静态化/部分页面静态化;
或者将一些数据存在memcache或者redis中,不用去查表
数据一致性处理
当多个进程同时操作同一个数据,会产生资源争抢,数据一致性的问题。
高并发情况下,涉及到写操作时,不可能直接操作数据库,大并发的连接会导致mysql请求会阻塞,比如大量的insert update 请求到,会直接导致无数的行锁和表锁,甚至最后堆积很多,从来触发too many connections 错误。
web服务器 nginx和apache连接的进程有限,cpu上下文进程切换也会增加额外的开销,所以响应一定快。
这时可以采用
高并发下的数据安全,防超发,以抢票系统为例:
(1)消息队列:
将票数资源存在redis中,将请求存入消息队列(redis下的list阻塞的,可以实现消息队列,还可以实现优先消息队列点击打开链接)中,依次处理。缺点 :这样会处理比较慢,等待时间比较长。
:对于读操作是否也进入队列,这个问题根据具体的场景,像12306应该是不在队列中或者是优先排在最前面的,因为只是读,要求块。
(2)加锁
常见的锁: 排它锁;乐观锁;悲观锁;
排他锁:在进行写时,禁止一切的读和写;
乐观锁:认为在写的时候,别人不在写,维护一个version号,等处理后对照version好,一致则对,否则回滚,操作不成功,
悲观锁:认为在写的时候,别人也在写。采用数据库提供的锁机制:在写操作的时(insert updata 等)myisam默认是锁表,innodb根据是否是主键,主键则行锁,否则表锁。读操作,innodb采用mvcc版本控制。
可以采用乐观锁+回滚:
采用悲观锁: