索引类型,实现原理
B+树:左边小,右边大,只有叶子节点包含key和数据 ,同时叶子节点有链指针,方便区间查询数据。好处是减小节点大小,降低磁盘IO。Innodb引擎下数据文件本身就是索引文件
聚簇索引:叶子节点就是数据节点
非聚簇索引:非聚簇索引的叶子节点仍然是索引节点,只不过有指向对应数据块的指针
覆盖索引:指一个查询语句的数据只用从索引中就能获得,不必从数据库表中获取
读写分离
同步
从库开一个线程主动从主库拉取日志保存到本地(relay_log),再单独开一个线程从relay日志保存到本地
半同步
主库写入binlog后强制同步日志到从库,从库写入日志成功后返回ACK确认给主库,主库收到至少一个从库的确认才认为写操作完成(防止主库宕机导致数据丢失)
主从延迟解决方案
分库,主库拆分,降低主库的写并发
设置强制走主库查询
事务的基本特性,隔离级别
ACID:原子性、一致性、持久性、隔离性
怎么保证ACID
原子性:undo log保证,事务回滚撤销已经执行成功的sql
一致性:代码层面判断
持久性:redo log保证 内存+redo log,宕机从redo log恢复写入磁盘
隔离性:MVCC
MVCC、隔离级别
1、read uncommit(读未提交) 读到其他事务未提交的数据 脏读
2、read commit(读已提交) 两次读取结果不一致 不可重复读
3、repeatable read(可重复复读)mysql 默认级,每次读取结果都一样 幻读
实现原理:MVCC 多版本并发控制,行数据隐藏了两列创建事务id,删除事务id,查询只会查询创建时间事务id<当前事务id的行,所有能保证每次读取的数据是一样的,但是插入或者删除数据并且同时在做范围查询的时候无法保证
幻读条件:做查询,其他事务做了insert或者delete,必须是一个范围。同样的查询条件查询到不同的结果
4、serializable 串行
锁的类型,原理
表锁 :alter修改表等操作
行锁:
1、for update 悲观锁 排他锁
2、version版本乐观锁
3、lock in share mode 共享锁 读锁,只能读不能写
4、页锁
数据库优化从哪些方面
where条件具体
where条件及排序使用索引
避免在where条件进行null判断 is null
避免在where条件中使用or连接条件
避免在where条件中使用in not in 对于连续值使用between
避免like'%%'
避免在条件中使用表达式
避免在条件中使用函数
避免索引过多,不必要的索引不要
尽可能避免更新索引列数据
避免使用force_index
分库分表
顺序:先垂直、后水平
垂直拆分
垂直分表
大表拆小表,基于字段进行。一般表中字段较多,将不常用的、数据较大、长度较长的拆分到扩展表
垂直分库
针对系统中不同业务拆分,比如用户、商品、订单,垂直的维度现在基本不用考虑了,因为微服务已经是在做这样一件事情了。
水平拆分
针对数据量巨大的表,根据range、hash等某种规则拆分到多张表
-
RANGE
-
从id 0-1000一张表,1001-2000一张表
-
HASH取摸
-
如用户id hash取模,分配到不同数据库
-
地理位置
-
时间
-
历史数据归档,比如半年前数据归档
一般来说都是基于hash取模的方式来做分表,再加上定期的数据归档来达到目标。
分表后非sharding_key字段查询
在分表之后,我们的查询理论上所有的查询都需要根据sharding_key去查询数据,比如订单表根据用户id作为sharding_key分表,那么针对C端的场景查询都是基于用户id维度的这个很好办,但是对于B端需要根据商户或者其他维度的查询怎么解决?
1、落两份数据,走不同库查询,针对B端和C端的场景区分开
2、关联表,针对其他场景的字段做关联关系,先从中间表查询。这个方式其实在设计数据库的时候可以通过表的设计来做到,不过业务太复杂的话也是不好处理的。
3、索引、多线程扫全表。需要的字段建好索引,去扫全表的话针对B端的场景也是可以接受的
4、数仓,数据库不是很大的话,一般的离线数仓其实也可以作为准实时的来用,就是价格贵就是了。业务不是很大的公司效果其实还可以接受。