数据库
redis
数据结构
-
strings
-
hashes 可以只修改一个属性
-
lists 双端链表
-
sets
-
sorted sets
特点
快:单线程(避免线程切换消耗)、内存、多路复用IO
持久化方式:RDB+AOF,RDB快照,可能丢失。AOF类似binlog
主从同步原理:从初次接入master后先将主的RDB文件下载到本地然后加载进缓存进行全量初始化。后面主会将写命令同步给从slaver
高可用方案
哨兵+分片(redis cluster)+多【主-从】
缓存击穿方案
缓存击穿: key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
-
场景:热点Key的失效瞬间导致直接请求数据库。处理:不更新的缓存设置永久有效。分布式锁方案,在失效后可以阻塞等待缓存从数据库查回来
-
分布式部署redis
方案:使用互斥锁(mutex key) 这种思路比较简单,就是让一个线程回写缓存,其他线程等待回写缓存线程执行完,重新读缓存即可。 同一时间只有一个线程读数据库然后回写缓存,其他线程都处于阻塞状态。 如果是分布式应用就需要使用分布式锁。
缓存雪崩方案
缓存雪崩:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉.
1事前 高可用部署方案 2 事中hystrix限流降级避免打死mysql 3事后 备份,重启快速恢复
方案:均匀过期、加互斥锁、缓存永不过期、双层缓存策略(1)过期 设置不同的过期时间,让缓存失效的时间点尽量均匀。通常可以为有效期增加随机值或者统一规划有效期 (2)加锁 跟缓存击穿解决思路一致,同一时间只让一个线程构建缓存,其他线程阻塞排队。 (3)不过期 跟缓存击穿解决思路一致,缓存在物理上永远不过期,用一个异步的线程更新缓存 (4)双层存策略 使用主备两层缓存: 主缓存:有效期按照经验值设置,设置为主读取的缓存,主缓存失效后从数据库加载最新值. 备份缓存:有效期长,获取锁失败时读取的缓存,主缓存更新时需要同步更新备份缓存.
缓存穿透方案
缓存穿透: key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
数据库查不到就在缓存存一个有效时间空值,下次就会直接读取缓存
方案:布隆过滤器(错误率换取空间)
Mysql
索引
原理
生产环境的 B+树索引有多少层 般是2~3层,可以存放约 两千万行的数据。(2层大概几万条)一层叶子 两层非聚簇索引
B-tree利用了磁盘块的特性进行构建的树。每个磁盘块一个节点,每个节点包含了很关键字。把树的节点关键字增多后树的层级比原来的二叉树少了,减少数据查找的次数和复杂度。B-tree巧5妙利用了磁盘预读原理,将一个节点的大小设为等于一个页(每页为4K),这样每个节点只需要一次IO就可以完全载入。B-tree的数据可以存在任何节点中。
B+tree:B+tree是B-tree的变种,B+tree数据只存储在叶子节点中。这样在B树的基础上每个节点存储的关键字数更多树的层级更少所以查询数据更快,所有指关键字指针都存在叶子节点,所以每次查找的次数都相同所以查询速度更稳定
类型
-
聚簇索引:一般指主键索引,可以通过索引物理顺序直接找到数据页,而非聚簇索引一般指存了聚簇索引的索引页,不能直接指向数据页可能需要回表
-
主键索引
-
非唯一索引
-
唯一索引
-
B+索引
行锁
mysql行锁基于索引实现,只有用到索引才能触发
什么时候用表锁:更新全表或多表关联更新防止死锁
执行流程
1、查询缓存
2、解析器生成解析树
3、预处理再次生成解析树
4、查询优化器
5、查询执行计划
6、查询执行引擎
7、查询数据返回结果
事务
特性:原子性、一致性、 持久性、隔离性
隔离级别:read uncommit、read commited、repeatable read、serializable
JOIN原理
-
JOIN的原理: 在mysql中使用Nested Loop Join来实现join; A JOIN B:通过A表的结果集作为循环基础,一条一条的通过结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果;
-
JOIN的优化原则:
-
尽可能减少Join 语句中的Nested Loop 的循环总次数,用小结果集驱动大结果集;
-
优先优化Nested Loop 的内层循环;
-
保证Join 语句中被驱动表上Join 条件字段已经被索引;
-
扩大join buffer的大小;
-
sql执行顺序
from,join,on,where,group by,sum,having,select,distinct,order by
日志
数据库事务特性的实现原理 在说明原子性原理之前,首先介绍一下 MySQL 的事务日志。MySQL 的日志有很多种,如二进制日志、错误日志、查询日志、慢查询日志等,此外InnoDB 存还提供了两种事务日志: redo log (重做日志)和undo log (回滚日志)。其中
redo log 用于保证事务持久性;
undo log 则是事务原子性和隔离性实现的基础
在 MySQL 中还存在 bin log (二进制志也可以记录写操作并用于数据的恢复,但二者是有着根本的不同的
作用不同: redo log 是用于事故恢复的,保证 MySQL 宕机也不会影响持久性; bin log是用于时间点恢复的,保证服务器可以基于时间点恢复数据,此外 bin log 还用于主从复制
层次不同: redo log 是lnnoDB 索引实现的,而 bin log 是 MySQL 的服务器层实现的,同时支持 nnoDB 和 其他存储引擎
写入时机不同: bin log 在事务提交时写入; redo log 的写入时机相对多元 MVCC
undo log
undo log 是实现原子性的关键,当事务回滚时能够撤销所有已经成功执行的 SQL 语句。lnnoDB 实现回滚,靠的就是
undo log 属于逻辑日志,它记录的是 SQL 执行的相关信息。当发生回滚时,lnnoDB 会根据undolog 的内容做与之前相反的工作
redolog
持久性 redolog 存在的背景:lnnoDB 作为 MySQL 的存储引,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘10效率会很低,为此 nnoDB 提供了缓存 Bufer Pool,Buffer Pool中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当从数据库读取数据时,会首先从 Buffer Pool 中读取,如果 Buffer Pool 中没有,则从磁盘读取后放入 BufferPool;当向数据库写入数据时,会首先写入 Buffer Pool,Buffer Pool 中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)
MVCC机制
MVCC 全称 Multi-Version Concurrency Control,即多版本的并发控制协议。下面的例子很好的体现了 MVCC 的特点:在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)--在 T5 时刻,事务 和事务C 可以读取到不同版本的数据
主从同步原理
从库启线程同步主binlog日志到本地后执行。另外同步方式有半同步复制和并行复制方式,半同步是复制后回传同步标识,解决数据丢失问题。并行是多从一起复制
线上问题排查
show full processlist查看目前在运行的sql
explain 执计划
-
type,查询类型
-
key,优化器采用的索引
-
possible key
-
rows查询需要检索的数量
-
key len: 使用的索引左前缀的长度
show命令
-
show global status like ‘Slow_queries’ 查询慢sql的个数
-
show variables like 'long_query_time' 查看慢查询相关配置
-
show variables like 'innodb_buffer_pool_size' 查看innodb缓存
-
show status like 'Innodb_buffer_pool%' 查看缓存使用状态
-
缓存命中率= (1-Innodb_buffer_pool_reads/Innodb_buffer_pool_read_requests);
-
缓存率=(Innodb_buffer_pool_pages_data/Innodb_buffer_pool_pages_total)
-
-
show profiles 执行过程中各资源消耗情况(会话级)
-
show engine9 innodb status 分析死锁 ,需要super权限
数据库开发规范
基础规范
-
必须使用InnoDb:支持事务、行级锁、并发性能好,cpu以及内存缓存页使得资源利用率高
-
必须使用UTF-8字符集:万国码,无需转码、无乱码风险、节省空间
-
数据库表字段必须加中文注释
-
禁止使用存储过程、视图、触发器、Event,高并发大数据互联网业务,架构设计思想是解放数据库CPU,将计算移至服务层,并发大时这些功能可能拖死数据库,业务逻辑放到服务层具备更好扩展性,能轻易实现加机器就能加性能。数据库擅长存储和索引,计算上移
-
禁止存储大文件,附件应该用url形式
命名规范
-
只允许使用内网域名而不是ip访问数据库
-
域名区分线上环境、开发环境和测试环境,从库加-s标识,备库加-ss标识
-
库名、表名、字段名:小写,下划线风格,不超过32字符,必须见名知意,禁止拼音英文混用
-
表名t,非唯一索引idx,唯一索引uniq
-
单实例表数小于500
-
单表列数小于30M
-
表必须有主键,例如自增
-
主键递增,数据写入可以提高性能。避免page分页,减少表碎片,提升空间和内存使用
-
索引较短,减少磁盘空间,提高索引缓存效率
-
无主键的表删除,在ow模式的主从架构,会导致备库夯住
-
-
禁止使用外建:关联应该用业务进行过解读,外建会导致表之间耦合,update和delete会涉及相关表,十分影响性能
字段设计规范
-
必须把字段定义为not null。并且提供默认值
-
null的列使索引、索引统计、值比较都更加复杂,对于mysql更难优化
-
null类型mysql内部需要特殊处理,增加数据库记录复杂性;同等条件下,表中有较多空字段的时候,数据库处理性能会降低很多
-
nul值需要更多的存储空间,无论表还是索引每行的nul列都需要额外空间标识
-
对null处理的时候,只能用is null 或 is not null,不能用=、> 、!=等操作符。如 where name !=“jiang”如果name有空值,结果不会包含name为null的记录
-
-
禁止使用TEXT、BLOB:浪费更多磁盘空间和内存,大字段查询会淘汰热数据,降低内存命中率,影响性能
-
禁止用小数存货币,容易对不上
-
使用varchar(20)存手机号:区号或国家代号。手机号不需计算。支持模糊查询
-
禁止用ENUM,增加需要DDL操作,实质还是存的整数
-
应用程序需要捕获异常,并处理
-
索引
-
单表索引控制在5个内
-
禁止在更新频繁、区分度不高的列建立索引:更新会变更B+树,降低数据库性能
-
单索引字段数控制在5个内
-
组合索引,区分度高的放前面:最左匹配原则,有效过滤
-
禁止使用select *,只获取必要字段,显示说明列属性:读取不需要的列会增加IO、CPU、NET消耗。不能有效利用覆盖索引。容易在增加或删除出现BUG
-
禁止使用insert 表名,需显示指定插入的列属性
-
禁止使用jion查询,禁止大表使用子查询:产生临时表,消耗cpu内存
-
索引失效
-
禁止使用or需要改为in:索引失效
-
禁止在where条件属性上加函数或表达式
-
禁止负向查询(not in,!=等)或%开头模糊查询
-
禁止使用隐式转换:索引失效
-
-
应用程序需要捕获异常,并处理
-