数据库事务ACID原则
数据库事务正确执行的四个原则:A原子性、C一致性、I独立性、D持久性
原子性
整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没发生过一样
一致性
事务开始之前和事务结束后,数据库的完整性约束没有被破坏
独立性
事务的执行互不干扰
持久性
事务执行成功后,该事务对数据库的更改是持久保存在数据库中的,不会被回滚
乐观锁和悲观锁的区别
悲观锁(Pessimistic Lock)
悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作。 通常所说的“一锁二查三更新”即指的是使用悲观锁。 乐观锁(Optimistic Lock) 乐观锁的特点先进行业务操作,不到万不得已不去拿锁。 即“乐观”的认为拿锁多半是会成功的,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好。 乐观锁在数据库上的实现完全是逻辑的,不需要数据库提供特殊的支持。 一般的做法是在需要锁的数据上增加一个版本号,或者时间戳,然后按照如下方式实现: 1. SELECT data AS old_data, version AS old_version FROM …; 2. 根据获取的数据进行业务操作,得到new_data和new_version 3. UPDATE SET data = new_data, version = new_version WHERE version = old_version if (updated row > 0) { // 乐观锁获取成功,操作完成 } else { // 乐观锁获取失败,回滚并重试 } 乐观锁是否在事务中其实都是无所谓的, 其底层机制是这样:在数据库内部update同一行的时候是不允许并发的, 即数据库每次执行一条update语句时会获取被update行的写锁,直到这一行被成功更新后才释放。 因此在业务操作进行前获取需要锁的数据的当前版本号,然后实际更新数据时再次对比版本号确认与之前获取的相同, 并更新版本号,即可确认这之间没有发生并发的修改。 如果更新失败即可认为老版本的数据已经被并发修改掉而不存在了,此时认为获取锁失败,需要回滚整个业务操作并 可根据需要重试整个过程。
数据库隔离级别是什么?有什么作用
脏读即为session A读取到了session B中未提交的数据
不可重复读即为session A读取到了session B提交的数据,即前后session A读取的数据不一致
幻读即为session A读取到了session B insert的数据
oracle默认的隔离级别为 读已提交,mysql的默认隔离级别为 可重复读
SQL什么情况下不会使用索引
1、查询谓词没有使用索引的主要边界,换句话说就是select *,可能会导致不走索引。
2、单键值的b树索引列上存在null值,导致COUNT(*)不能走索引。
如果在B树索引中有一个空值,那么查询诸如SELECT COUNT(*) FROM T 的时候,因为HASHSET中不能存储空值的,所以优化器不会走索引,有两种方式可以让索引有效,一种是SELECT COUNT(*) FROM T WHERE XXX IS NOT NULL或者把这个列的属性改为not null (不能为空)。
3、索引列上有函数运算,导致不走索引
如果在T表上有一个索引Y,但是你的查询语句是这样子SELECT * FROM T WHERE FUN(Y) = XXX。这个时候索引也不会被用到,因为你要查询的列中所有的行都需要被计算一遍,因此,如果要让这种sql语句的效率提高的话,在这个表上建立一个基于函数的索引,比如CREATE INDEX IDX FUNT ON T(FUN(Y));这种方式,等于Oracle会建立一个存储所有函数计算结果的值,再进行查询的时候就不需要进行计算了,因为很多函数存在不同返回值,因此必须标明这个函数是有固定返回值的。
4、隐式转换导致不走索引。
索引不适用于隐式转换的情况,比如你的SELECT * FROM T WHERE Y = 5 在Y上面有一个索引,但是Y列是VARCHAR2的,那么Oracle会将上面的5进行一个隐式的转换,SELECT * FROM T WHERE TO_NUMBER(Y) = 5,这个时候也是有可能用不到索引的。
5、表的数据库小或者需要选择大部分数据,不走索引
6、cbo优化器下统计信息不准确,导致不走索引
很长时间没有做表分析,或者重新收集表状态信息了,在数据字典中,表的统计信息是不准确的,这个情况下,可能会使用错误的索引,这个效率可能也是比较低的
7、!=或者<>(不等于),可能导致不走索引,也可能走 INDEX FAST FULL SCAN
8、表字段的属性导致不走索引,字符型的索引列会导致优化器认为需要扫描索引大部分数据且聚簇因子很大,最终导致弃用索引扫描而改用全表扫描方式
一般在什么字段上建索引
1. 表的主键、外键必须有索引
2. 经常与其他表进行连接的表,在连接字段上应该建立索引
3. 经常出现在where字句中的字段,特别是大表的字段,应该建立索引
4. 索引应该建在选择型高的字段上
5. 索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引
6. 复合索引的建立需要进行仔细分析,尽量考虑使用单字段索引代替:
① 正确选择复合索引中的主列字段,一般是选择性较好的字段
② 复合索引的几个字段是否经常同时以AND方式出现在where子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引;
③ 如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;
④ 如果复合索引所包含的字段超过3个,那么仔细考虑其必要性,考虑减少复合的字段;
7. 频繁进行数据操作的表,不要建立太多的索引;
如何优化数据库性能
索引、分库分表、批量操作、分页算法、升级硬盘SSD、业务优化、主从部署
Redis,RDB和AOF
RDB 是 Redis 默认的持久化方案。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件恢复数据。
优点:
1 适合大规模的数据恢复。
2 如果业务对数据完整性和一致性要求不高,RDB是很好的选择。
缺点:
1 数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。
2 备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件。
所以Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。
AOF :Redis 默认不开启。它的出现是为了弥补RDB的不足(数据的不一致性),所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
优点:数据的完整性和一致性更高
缺点:因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢
Redis如何做高可用、集群
Redis集群方案历史上有3个版本:Sentinel版本、Sentinel+Proxy版本,Cluster版本
- Sentinel版本:一主N从,由Sentinel做监控和主从切换
- Sentinel+Proxy版本:N主M从,Proxy负责做分片计算
- Cluster的方案(Redis 3.0以后开始支持),Cluster比较牛逼,Proxy(key映射分片)和Sentinel(主从切换)的功能都自己做了
Sentinel模型
image
如图所示,有一个虚拟IP (VIP),Sentinel会指定一个Node为Master,并且让VIP指向该Node,从而实现主从切换
Sentinel的作用有三个:
- 监控:Sentinel 会不断的检查主服务器和从服务器是否正常运行。
- 通知:当被监控的某个Redis服务器出现问题,Sentinel通过API脚本向管理员或者其他的应用程序发送通知。
- 自动故障转移:当主节点不能正常工作时,Sentinel会开始一次自动的故障转移操作,它会将与失效主节点是主从关系的其中一个从节点升级为新的主节点,并且将其他的从节点指向新的主节点。
缺陷:
- 主从切换的过程中会丢数据(因为Master 和Slave的数据不能100%同步)
- Redis只能单点写,不能水平扩容,集群中只能有一个master
Cluster特性(Redis 3.0以上版本支持)
- 1个集群支持多Master(分片),每个Master支持多Slave,Cluster自己负责主从切换
- Cluster支持一定的容错性,只要过半Master工作,整个Redis集群就可用
- 根据公式HASH_SLOT=CRC16(key) mod 16384 计算出Slot,再计算出映射到哪个分片上,然后Redis会去相应的节点进行操作
- 可以通过hashTag,让某几个key保存在同一个slot上,因为做Slot分配的时候,只计算 {keyHead}KeyTail 括号里面的keyHead,比如 {foo}Lion ,{foo}dear,只有在一个Node上,才能确保事务
- 客户端与Redis节点直连,不需要中间Proxy层,直接连接任意一个Master节点
- Cluster支持热扩展,从集群中新增或者删除一个分片,不需要重启,会自动重新分配Slot到对应的分片上
集群的原理
- 任何两个节点之间都是相互连通的。客户端可以与任何一个节点相连接,然后就可以访问集群中的任何一个节点。对其进行存取和其他操作。
- 它们之间通过互相的ping-pong判断是否节点可以连接上。如果有一半以上的节点去ping一个节点的时候没有回应,集群就认为这个节点宕机了,然后去连接它的备用节点。如果某个节点和所有从节点全部挂掉,我们集群就进入faill状态
MySQL,B+索引实现
B树是一种多路自平衡搜索树,它类似普通的二叉树,但是B书允许每个节点有更多的子节点。B树示意图如下:
B树的特点:
(1)所有键值分布在整个树中
(2)任何关键字出现且只出现在一个节点中
(3)搜索有可能在非叶子节点结束
(4)在关键字全集内做一次查找,性能逼近二分查找算法
B+树是B树的变体,也是一种多路平衡查找树,B+树的示意图为:
从图中也可以看到,B+树与B树的不同在于:
(1)所有关键字存储在叶子节点,非叶子节点不存储真正的data
(2)为所有叶子节点增加了一个链指针
为什么mysql的索引使用B+树而不是B树呢??
(1)B+树更适合外部存储(一般指磁盘存储),由于内节点(非叶子节点)不存储data,所以一个节点可以存储更多的内节点,每个节点能索引的范围更大更精确。也就是说使用B+树单次磁盘IO的信息量相比较B树更大,IO效率更高。
(2)mysql是关系型数据库,经常会按照区间来访问某个索引列,B+树的叶子节点间按顺序建立了链指针,加强了区间访问性,所以B+树对索引列上的区间范围查询很友好。而B树每个节点的key和data在一起,无法进行区间查找。
MySQL主备同步的基本原理
从库生成两个线程,一个I/O线程,一个SQL线程;
i/o线程去请求主库 的binlog,并将得到的binlog日志写到relay log(中继日志) 文件中;
主库会生成一个 log dump 线程,用来给从库 i/o线程传binlog;
SQL 线程,会读取relay log文件中的日志,并解析成具体操作,来实现主从的操作一致,而最终数据一致
mysql主从复制 灵活
● 一主一从
● 主主复制
● 一主多从---扩展系统读取的性能,因为读是在从库读取的;
● 多主一从---5.7开始支持
● 联级复制---
MySQL InnoDB的特点
InnoDB引擎特点
1.支持事务,支持4个事务隔离级别,支持多版本读。
2.行级锁定(更新时一般是锁定当前行),通过索引实现,全表扫描仍然会是表锁,注意间隙锁的影响。
3.读写阻塞与事务隔离级别相关。
4.具有非常高效的缓存特性:能缓存索引,也能缓存数据。
5.整个表和主键以Cluster方式存储,组成一个平衡树。
6.所有Secondary Index都会保存主键信息。
7.支持分区,表空间,类似oracle数据库。
8.支持外键约束,5.5之前不支持全文索引,5.5之后支持外键索引。
小结:supports transactions,row-level locking。and foreign keys
9.和Myisam引擎比,Innodb对硬件资源要求比较高。
MySQL引擎之innodb引擎应用场景及调优
Innodb引擎适用的生产场景
1、需要事务支持的业务(具有较好的事务特性)
2、行级锁定对高并发有很好的适应能力,但需要确保查询时通过索引完成。
3、数据读写及更新都较为频繁的场景,如:bbs,sns,微博,微信等。
4、数据一致性要求较高的业务,例如:充值转账,银行卡转账。
5、硬件设备内存较大,可以利用Innodb较好的缓存能力来提高内存利用率,尽可能减少磁盘IO。