数据库优化

mysql

MYSQL redo_log原理(实现D),undo_log原理(实现A)

大数据量的分页查询怎么优化

1.子查询优化
2.覆盖索引
3.使用id限定优化
https://segmentfault.com/a/1190000038856674
https://www.cnblogs.com/jelly12345/p/14242428.html

分库分表怎么做,可能会遇到什么问题

1.垂直分库分表,水平分库分表
https://www.infoq.cn/article/key-steps-and-likely-problems-of-split-table
https://zhuanlan.zhihu.com/p/54921615

skiplist与平衡树、哈希表的比较

skiplist和各种平衡树(如AVL、红黑树等)的元素是有序排列的,而哈希表不是有序的。因此,在哈希表上只能做单个key的查找,不适宜做范围查找。所谓范围查找,指的是查找那些大小在指定的两个值之间的所有节点。
在做范围查找的时候,平衡树比skiplist操作要复杂。在平衡树上,我们找到指定范围的小值之后,还需要以中序遍历的顺序继续寻找其它不超过大值的节点。如果不对平衡树进行一定的改造,这里的中序遍历并不容易实现。而在skiplist上进行范围查找就非常简单,只需要在找到小值之后,对第1层链表进行若干步的遍历就可以实现。
平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。
从内存占用上来说,skiplist比平衡树更灵活一些。一般来说,平衡树每个节点包含2个指针(分别指向左右子树),而skiplist每个节点包含的指针数目平均为1/(1-p),具体取决于参数p的大小。如果像Redis里的实现一样,取p=1/4,那么平均每个节点包含1.33个指针,比平衡树更有优势。
查找单个key,skiplist和平衡树的时间复杂度都为O(log n),大体相当;而哈希表在保持较低的哈希值冲突概率的前提下,查找时间复杂度接近O(1),性能更高一些。所以我们平常使用的各种Map或dictionary结构,大都是基于哈希表实现的。
从算法实现难度上来比较,skiplist比平衡树要简单得多。

总结起来,Redis中的skiplist跟前面介绍的经典的skiplist相比,有如下不同:
分数(score)允许重复,即skiplist的key允许重复。这在最开始介绍的经典skiplist中是不允许的。
在比较时,不仅比较分数(相当于skiplist的key),还比较数据本身。在Redis的skiplist实现中,数据本身的内容唯一标识这份数据,而不是由key来唯一标识。另外,当多个元素分数相同的时候,还需要根据数据内容来进字典排序。
第1层链表不是一个单向链表,而是一个双向链表。这是为了方便以倒序方式获取一个范围内的元素。
在skiplist中可以很方便地计算出每个元素的排名(rank)。

https://zhuanlan.zhihu.com/p/23370124

乐观锁和悲观锁,并发情况下,非要在这两个中选择一个的话,选哪个

实际生产环境里边,如果并发量不大,完全可以使用悲观锁定的方法,这种方法使用起来非常方便和简单。但是如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以就要选择乐观锁定的方法。
悲观锁假定其他用户企图访问或者改变你正在访问、更改的对象的概率是很高的,因此在悲观锁的环境中,在你开始改变此对象之前就将该对象锁住,并且直到你提交了所作的更改之后才释放锁。悲观的缺陷是不论是页锁还是行锁,加锁的时间可能会很长,这样可能会长时间的限制其他用户的访问,也就是说悲观锁的并发访问性不好。
乐观锁则认为其他用户企图改变你正在更改的对象的概率是很小的,因此乐观锁直到你准备提交所作的更改时才将对象锁住,当你读取以及改变该对象时并不加锁。可见乐观锁加锁的时间要比悲观锁短,乐观锁可以用较大的锁粒度获得较好的并发访问性能。
但是如果第二个用户恰好在第一个用户提交更改之前读取了该对象,那么当他完成了自己的更改进行提交时,数据库就会发现该对象已经变化了,这样,第二个用户不得不重新读取该对象并作出更改。这说明在乐观锁环境中,会增加并发用户读取对象的次数。

乐观锁有:cas和版本管理系统两个方案。
https://blog.csdn.net/btb5e6nsu1g511eg5xeg/article/details/86653497
https://zhuanlan.zhihu.com/p/40211594
https://www.cnblogs.com/kismetv/p/10787228.html#t5

MySQL中的for update

InnoDB默认是行级别的锁,当有明确指定的主键时候,是行级锁。否则是表级别。
for update 仅适用于InnoDB,并且必须开启事务,在begin与commit之间才生效。
1.只根据主键进行查询,并且查询到数据,主键字段产生行锁。
2.只根据主键进行查询,没有查询到数据,不产生锁。
3.根据主键、非主键含索引(name)进行查询,并且查询到数据,主键字段产生行锁,name字段产生行锁。
4.根据主键、非主键含索引(name)进行查询,没有查询到数据,不产生锁。
5.根据非主键不含索引字段进行查询,不管有没有查询到数据,该字段产生表锁。
6.只根据主键进行查询,查询条件为不等于、like,不管有没有查询到数据,主键字段产生表锁

可重复读解决了幻读?

https://zhuanlan.zhihu.com/p/360254683

索引覆盖了解吗

覆盖索引(covering index ,或称为索引覆盖)即从非主键索引中就能查到的记录,而不需要查询主键索引中的记录,避免了回表的产生减少了树的搜索次数,显著提升性能。

如何确定数据库成功使用了覆盖索引呢?
当发起一个索引覆盖查询时,在explain的extra列可以看到using index的信息
遇到以下情况,执行计划不会选择覆盖查询
1.select选择的字段中含有不在索引中的字段 ,即索引没有覆盖全部的列。
2.where条件中不能含有对索引进行like的操作。

https://juejin.cn/post/6844903967365791752
https://www.jianshu.com/p/77eaad62f974

间隙锁

间隙锁(Gap Lock)是Innodb在提交下为了解决幻读问题时引入的锁机制,(下面的所有案例没有特意强调都使用可重复读隔离级别)幻读的问题存在是因为新增或者更新操作,这时如果进行范围查询的时候(加锁查询),会出现不一致的问题,这时使用不同的行锁已经没有办法满足要求,需要对一定范围内的数据进行加锁,间隙锁就是解决这类问题的。在可重复读隔离级别下,数据库是通过行锁和间隙锁共同组成的(next-key lock),来实现的

加锁规则有以下特性,我们会在后面的案例中逐一解释:

1.加锁的基本单位是(next-key lock),他是前开后闭原则
2.插叙过程中访问的对象会增加锁
3.索引上的等值查询–给唯一索引加锁的时候,next-key lock升级为行锁
4.索引上的等值查询–向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁
5.唯一索引上的范围查询会访问到不满足条件的第一个值为止
https://www.jianshu.com/p/32904ee07e56

跨表分页查询怎么做

方法一:全局视野法
(1)将order by time offset X limit Y,改写成order by time offset 0 limit X+Y
(2)服务层对得到的N*(X+Y)条数据进行内存排序,内存排序后再取偏移量X后的Y条记录
这种方法随着翻页的进行,性能越来越低。
方法二:业务折衷法-禁止跳页查询
(1)用正常的方法取得第一页数据,并得到第一页记录的time_max
(2)每次翻页,将order by time offset X limit Y,改写成order by time where time>$time_max limit Y
以保证每次只返回一页数据,性能为常量。
方法三:业务折衷法-允许模糊数据
(1)将order by time offset X limit Y,改写成order by time offset X/N limit Y/N
方法四:二次查询法
(1)将order by time offset X limit Y,改写成order by time offset X/N limit Y
(2)找到最小值time_min
(3)between二次查询,order by time between $time_min and $time_i_max
(4)设置虚拟time_min,找到time_min在各个分库的offset,从而得到time_min在全局的offset
(5)得到了time_min在全局的offset,自然得到了全局的offset X limit Y
https://cloud.tencent.com/developer/article/1048654

mysql主从复制怎么做的,出现网络波动怎么

MySQL 主从复制涉及到三个线程:
一个在主节点的线程:log dump thread
从库会生成两个线程:一个 I/O 线程,一个 SQL 线程
如下图所示:
在这里插入图片描述
主库会生成一个 log dump 线程,用来给从库 I/O 线程传 Binlog 数据。
从库的 I/O 线程会去请求主库的 Binlog,并将得到的 Binlog 写到本地的 relay log (中继日志)文件中。
SQL 线程,会读取 relay log 文件中的日志,并解析成 SQL 语句逐一执行。
主节点 log dump 线程
当从节点连接主节点时,主节点会为其创建一个 log dump 线程,用于发送和读取 Binlog 的内容。在读取 Binlog 中的操作时,log dump 线程会对主节点上的 Binlog 加锁;当读取完成发送给从节点之前,锁会被释放。主节点会为自己的每一个从节点创建一个 log dump 线程。
从节点I/O线程
当从节点上执行start slave命令之后,从节点会创建一个 I/O 线程用来连接主节点,请求主库中更新的Binlog。I/O 线程接收到主节点的 log dump 进程发来的更新之后,保存在本地 relay-log(中继日志)中。
relay log
这里又引申出一个新的日志概念。MySQL 进行主主复制或主从复制的时候会在要复制的服务器下面产生相应的 relay log。
https://www.cnblogs.com/rickiyang/p/13856388.html

Statement 基于语句,只记录对数据做了修改的SQL语句,能够有效的减少binlog的数据量,提高读取、基于binlog重放的性能
Row 只记录被修改的行,所以Row记录的binlog日志量一般来说会比Statement格式要多。基于Row的binlog日志非常完整、清晰,记录了所有数据的变动,但是缺点是可能会非常多,例如一条update语句,有可能是所有的数据都有修改;再例如alter table之类的,修改了某个字段,同样的每条记录都有改动。
Mixed Statement和Row的结合,怎么个结合法呢。例如像update或者alter table之类的语句修改,采用Statement格式。其余的对数据的修改例如update和delete采用Row格式进行记录。
https://segmentfault.com/a/1190000038967218

redis的过期键删除策略

通常删除某个key,我们有如下三种方式进行处理。
①、定时删除
  在设置某个key 的过期时间同时,我们创建一个定时器,让定时器在该过期时间到来时,立即执行对其进行删除的操作。
  优点:定时删除对内存是最友好的,能够保存内存的key一旦过期就能立即从内存中删除。
  缺点:对CPU最不友好,在过期键比较多的时候,删除过期键会占用一部分 CPU 时间,对服务器的响应时间和吞吐量造成影响。
②、惰性删除
  设置该key 过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key。
  优点:对 CPU友好,我们只会在使用该键时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查。
  缺点:对内存不友好,如果一个键已经过期,但是一直没有使用,那么该键就会一直存在内存中,如果数据库中有很多这种使用不到的过期键,这些键便永远不会被删除,内存永远不会释放。从而造成内存泄漏。
③、定期删除
  每隔一段时间,我们就对一些key进行检查,删除里面过期的key。
  优点:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除,也能有效释放过期键占用的内存。
  缺点:难以确定删除操作执行的时长和频率。
     如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好。
     如果执行的太少,那又和惰性删除一样了,过期键占用的内存不会及时得到释放。
     另外最重要的是,在获取某个键时,如果某个键的过期时间已经到了,但是还没执行定期删除,那么就会返回这个键的值,这是业务不能忍受的错误。
  https://www.cnblogs.com/ysocean/p/12422635.html

redis缓存穿透你了解吗(把穿透击穿雪崩都讲了一遍)

缓存穿透:当查询Redis中没有的数据时,该查询会下沉到数据库层,同时数据库层也没有该数据,当这种情况大量出现或被恶意攻击时,接口的访问全部透过Redis访问数据库,而数据库中也没有这些数据,我们称这种现象为"缓存穿透"。
解决方案:
在接口访问层对用户做校验,如接口传参、登陆状态、n秒内访问接口的次数;
利用布隆过滤器,将数据库层有的数据key存储在位数组中,以判断访问的key在底层数据库中是否存在;
设置默认的缺省值或者空值
缓存击穿
缓存击穿和缓存穿透从名词上可能很难区分开来,它们的区别是:穿透表示底层数据库没有数据且缓存内也没有数据,击穿表示底层数据库有数据而缓存内没有数据。当热点数据key从缓存内失效时,大量访问同时请求这个数据,就会将查询下沉到数据库层,此时数据库层的负载压力会骤增,我们称这种现象为"缓存击穿"。
解决方案:
延长热点key的过期时间或者设置永不过期,如排行榜,首页等一定会有高并发的接口;
利用互斥锁保证同一时刻只有一个客户端可以查询底层数据库的这个数据,一旦查到数据就缓存至Redis内,避免其他大量请求同时穿过Redis访问底层数据库;
缓存雪崩
缓存雪崩是缓存击穿的"大面积"版,缓存击穿是数据库缓存到Redis内的热点数据失效导致大量并发查询穿过redis直接击打到底层数据库,而缓存雪崩是指Redis中大量的key几乎同时过期,然后大量并发查询穿过redis击打到底层数据库上,此时数据库层的负载压力会骤增,我们称这种现象为"缓存雪崩"。
还有可能是redis坏了
解决方案:
在可接受的时间范围内随机设置key的过期时间,分散key的过期时间,以防止大量的key在同一时刻过期;
对于一定要在固定时间让key失效的场景(例如每日12点准时更新所有最新排名),可以在固定的失效时间时在接口服务端设置随机延时,将请求的时间打散,让一部分查询先将数据缓存起来;
延长热点key的过期时间或者设置永不过期,这一点和缓存击穿中的方案一样;
服务降级:只给部分关键服务使用
服务熔断:直接报错(当redis节点宕机)
请求限流:
使用redis集群

https://cloud.tencent.com/developer/article/1666384

系统限流怎么做(做滑动窗口,漏桶,最后说说令牌桶)

计数器算法实现比较简单,特别适合集群情况下使用,但是要考虑临界情况,可以应用滑动窗口策略进行优化,当然也是要看具体的限流场景。
漏桶算法和令牌桶算法,漏桶算法提供了比较严格的限流,令牌桶算法在限流之外,允许一定程度的突发流量。在实际开发中,我们并不需要这么精准地对流量进行控制,所以令牌桶算法的应用更多一些。
如果我们设置的流量峰值是 permitsPerSecond=N,也就是每秒钟的请求量,计数器算法会出现 2N 的流量,漏桶算法会始终限制 N 的流量,而令牌桶算法允许大于 N,但不会达到 2N 这么高的峰值。
https://segmentfault.com/a/1190000038949009
https://www.jianshu.com/p/cd63c41c1ef6

布隆过滤器

优点
由于存储的是二进制数据,所以占用的空间很小
它的插入和查询速度是非常快的,时间复杂度是O(K),可以联想一下HashMap的过程
保密性很好,因为本身不存储任何原始数据,只有二进制数据
缺点
存在误判
删除困难
https://zhuanlan.zhihu.com/p/43263751

范围查询 b+树怎么实现的?

在这里插入图片描述
https://blog.csdn.net/qingdujun/article/details/80149056

说说你知道的HTTP攻击方式,挑一个详细说一下

Distributed Denial of Service (DDoS, 分布式拒绝服务)
跨站点请求伪造(CSRF,Cross-Site Request Forgeries)
SQL Injection (SQL 注入)
XSS攻击(Cross-Site scripting)
https://zhuanlan.zhihu.com/p/44302803

acdi以及MySQL怎么保证

原子性:回滚,undo log
持久性:宕机恢复,redo log(innodb特有)
InnoDB提供了缓存(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当从数据库读取数据时,会首先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。
Buffer Pool的使用大大提高了读写数据的效率,但是也带了新的问题:如果MySQL宕机,而此时Buffer Pool中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。
于是,redo log被引入来解决这个问题:当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记录这次操作;当事务提交时,会调用fsync接口对redo log进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。redo log采用的是WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到Buffer Pool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。
既然redo log也需要在事务提交时将日志写入磁盘,为什么它比直接将Buffer Pool中修改的数据写入磁盘(即刷脏)要快呢?主要有以下两方面的原因:
(1)刷脏是随机IO,因为每次修改的数据位置随机,但写redo log是追加操作,属于顺序IO。
(2)刷脏是以数据页(Page)为单位的,MySQL默认页大小是16KB,一个Page上一个小修改都要整页写入;而redo log中只包含真正需要写入的部分,无效IO大大减少。
redo log和bin log的区别
(1)作用不同:redo log是用于crash recovery的,保证MySQL宕机也不会影响持久性;binlog是用于point-in-time recovery的,保证服务器可以基于时间点恢复数据,此外binlog还用于主从复制。
(2)层次不同:redo log是InnoDB存储引擎实现的,而binlog是MySQL的服务器层(可以参考文章前面对MySQL逻辑架构的介绍)实现的,同时支持InnoDB和其他存储引擎。
(3)内容不同:redo log是物理日志,内容基于磁盘的Page;binlog的内容是二进制的,根据binlog_format参数的不同,可能基于sql语句、基于数据本身或者二者的混合。
(4)写入时机不同:binlog在事务提交时写入;redo log的写入时机相对多元
隔离性:写-写隔离用锁(行锁、表锁、next-key lock等),写-读隔离用mvcc
一致性:上面三个共同
https://juejin.cn/post/6844904095103320078

explain做了什么

列名 说明
id 执行编号,标识select所属的行。如果在语句中没子查询或关联查询,只有唯一的select,每行都将显示1。否则,内层的select语句一般会顺序编号,对应于其在原始语句中的位置
select_type 显示本行是简单或复杂select。如果查询有任何复杂的子查询,则最外层标记为PRIMARY(DERIVED、UNION、UNION RESUlT)
table 访问引用哪个表(引用某个查询,如“derived3”)
type 数据访问/读取操作类型(ALL、index、range、ref、eq_ref、const/system、NULL)
possible_keys 揭示哪一些索引可能有利于高效的查找
key 显示mysql决定采用哪个索引来优化查询
key_len 显示mysql在索引里使用的字节数
ref 显示了之前的表在key列记录的索引中查找值所用的列或常量
rows 为了找到所需的行而需要读取的行数,估算值,不精确。通过把所有rows列值相乘,可粗略估算整个查询会检查的行数
Extra 额外信息,如using index、filesort等

https://www.jianshu.com/p/ea3fc71fdc45

MVCC简单的原理,为了解决什么问题,MVCC的好处

常用的中文编码你知道吗,我们网站有泰语你认为应该用哪个

https://zhuanlan.zhihu.com/p/145325370

介绍下 select 和 epoll 以及它们的区别和各自的使用场景,epoll具体是怎么实现的,其红黑树结构存在于用户空间还是内核空间

慢sql 分析优化

对慢SQL优化一般可以按下面几步的思路:
1、开启慢查询日志,设置超过几秒为慢SQL,抓取慢SQL
2、通过explain对慢SQL分析(重点)
3、show profile查询SQL在Mysql服务器里的执行细节和生命周期情况(重点)
4、对数据库服务器的参数调优

优化思路
数据量:数据量越大需要的I/O次数越多
取数据的方式
数据在缓存中还是在磁盘上
是否可以通过索引快速寻址
数据加工的方式
排序、子查询等,需要先把数据取到临时表中,再对数据进行加工
增加了I/O,且消耗大量CPU资源

程序CPU占用特别高,怎么排查问题

MYSQL分布式锁,Redis分布式锁

mysql:排他锁(悲观锁),版本号(乐观锁),记录锁
https://segmentfault.com/a/1190000023045815
redis: setnx(单节点),Redlock(多节点)

分布式锁还有一个zookeeper:
https://zhuanlan.zhihu.com/p/51042458
https://juejin.cn/post/6844903729406148622
基于zookeeper临时有序节点可以实现的分布式锁。大致思想即为:每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。

数据库缓存一致性

先删缓存,再写数据库
或者
先写数据库,再删缓存
都有可能造成数据不一致问题

可以采用延迟双删策略,先删缓存,再写数据库,隔一段时间再删缓存。
https://xie.infoq.cn/article/45b71e537b123e62052d93f9e

延迟双删中第二次删除失败怎么办:
设置一个消息队列,将失败的id存进来,重试删除。
https://xie.infoq.cn/article/47241d099404a1565e168fad4

排序算法

通常快排比堆排要快的原因:快排是因为顺序访问数组,cpu cache命中率高;而堆排则是每次访问lchild = 2 * parent + 1; 相当于随机访问,cache命中率低

redis数据结构,Zset的底层结构

string:
在这里插入图片描述
在Redis中String是可以修改的,称为动态字符串(Simple Dynamic String 简称 SDS)(快拿小本本记名词,要考的),说是字符串但它的内部结构更像是一个 ArrayList,内部维护着一个字节数组,并且在其内部预分配了一定的空间,以减少内存的频繁分配。
应用场景:
存储key-value键值对,这个比较简单不细说了

list:
在这里插入图片描述
当数据量较少的时候它的底层存储结构为一块连续内存,称之为ziplist(压缩列表),它将所有的元素紧挨着一起存储,分配的是一块连续的内存;当数据量较多的时候将会变成quicklist(快速链表)结构。
可单纯的链表也是有缺陷的,链表的前后指针 prev 和 next 会占用较多的内存,会比较浪费空间,而且会加重内存的碎片化。在redis 3.2之后就都改用ziplist+链表的混合结构,称之为 quicklist(快速链表)。
应用场景
由于list它是一个按照插入顺序排序的列表,所以应用场景相对还较多的,例如:
消息队列:lpop和rpush(或者反过来,lpush和rpop)能实现队列的功能
朋友圈的点赞列表、评论列表、排行榜:lpush命令和lrange命令能实现最新列表的功能,每次通过lpush命令往列表里插入新的元素,然后通过lrange命令读取最新的元素列表。

hash:
在这里插入图片描述
应用场景:
购物车:hset [key] [field] [value] 命令, 可以实现以用户Id,商品Id为field,商品数量为value,恰好构成了购物车的3个要素。
存储对象:hash类型的(key, field, value)的结构与对象的(对象id, 属性, 值)的结构相似,也可以用来存储对象。

set:
在这里插入图片描述
应用场景:
好友、关注、粉丝、感兴趣的人集合:

  1. sinter命令可以获得A和B两个用户的共同好友;
  2. sismember命令可以判断A是否是B的好友;
  3. scard命令可以获取好友数量;
  4. 关注时,smove命令可以将B从A的粉丝集合转移到A的好友集合
    首页展示随机:美团首页有很多推荐商家,但是并不能全部展示,set类型适合存放所有需要展示的内容,而srandmember命令则可以从中随机获取几个。
    存储某活动中中奖的用户ID ,因为有去重功能,可以保证同一个用户不会中奖两次。

zset:
在这里插入图片描述
应用场景:
zset 可以用做排行榜,但是和list不同的是zset它能够实现动态的排序,例如: 可以用来存储粉丝列表,value 值是粉丝的用户 ID,score 是关注时间,我们可以对粉丝列表按关注时间进行排序。
zset 还可以用来存储学生的成绩, value 值是学生的 ID, score 是他的考试成绩。 我们对成绩按分数进行排序就可以得到他的名次。

https://i6448038.github.io/2019/12/01/redis-data-struct/
https://zhuanlan.zhihu.com/p/145384563

TCP如何做到可靠的

为什么mysql建议使用自增主键

1.从业务上来说
在设计数据库时不需要费尽心思去考虑设置哪个字段为主键。然后是这些字段只是理论上是唯一的,例如使用图书编号为主键,这个图书编号只是理论上来说是唯一的,但实践中可能会出现重复的情况。所以还是设置一个与业务无关的自增ID作为主键,然后增加一个图书编号的唯一性约束。
所有涉及到业务的字段,无论它看上去是否唯一,都决不能用作主键。例如,用户表的Email字段是唯一的,但是,如果用它作主键,就会导致其他表到处引用Email字段,从而泄露用户信息。
2.从技术上来说
如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。 总的来说就是可以提高查询和插入的性能。
如果主键是非自增 id,为了确保索引有序,mysql 就需要将每次插入的数据都放到合适的位置上。当往一个快满或已满的数据页中插入数据时,新插入的数据会将数据页写满,mysql 就需要申请新的数据页,并且把上个数据页中的部分数据挪到新的数据页上。
这就造成了页分裂,这个大量移动数据的过程是会严重影响插入效率的。
3.从磁盘的角度来说
由于MySQL从磁盘读取数据时一块一块来读取的,同时,根据局部性原理,MySQL引擎会选择预读一部分和你当前读数据所在内存相邻的数据块,这个时候这些相邻数据块的数据已经存在于内存中。由于数据库大部分是查询操作,这个时候,如果主键是自增的话,数据存储都是紧凑地存储在一起的,那么对于局部性原理利用和避免过多地I/O操作都有着巨大的促进作用

另一个回答:
1、自增主键的插入数据模式,正符合了我们前面提到的递增插入的场景。每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂、而有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高。
2、业务侧不需要为了主键的唯一性烦恼,MySQL自增ID保证了唯一性。

数据库的幂等操作在这里插入图片描述

https://juejin.cn/post/6906290538761158670

秒杀系统的设计

秒杀系统特征:
瞬间高并发请求、读多写少

秒杀系统设计:
1.秒杀活动前:把商详页等信息使用CDN或浏览器缓存起来。
2.秒杀活动开始:
库存查验、库存扣减、订单处理
库存查验、库存扣减:要在redis中原子处理,利用lua脚本或者分布式锁
订单处理:请求量少,可以放到后端数据库中
3.秒杀活动结束后:
退货、订单查询:访问量少,后端数据库可以处理。

此外还有一些关注点:
1.前端静态页面的设计。能静态化的都静态化。
2.请求拦截和流控。在接入层设置拦截恶意攻击。
3.库存信息过期处理。秒杀商品不设置过期时间。
4.订单异常处理。如果数据库没能成功处理订单,可以设置重试功能,保证订单最终能被处理。(使用消息队列)
参考极客时间。

隔离级别分别怎么实现

排他锁:
排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。
语法:select * from 表明 for update
共享锁:
共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
语法:select * from 表名 lock in share mode
注意:
1.使用排他锁后,普通的select查询不受影响,select在InnoDB引擎中默认不加锁

事务隔离级别通过锁的实现机制
两个锁:
排他锁 被加锁的对象只能被持有锁的事务读取和修改,其他事务无法在该对象上加其他锁,也不能读取和修改该对象
共享锁 被加锁的对象可以被持锁事务读取,但是不能被修改,其他事务也可以在上面再加共享锁。
特别的,对共享锁: 如果两个事务对同一个资源上了共享锁,事务A 想更新该数据,那么它必须等待 事务B 释放其共享锁。

未提交读:事务T在读取数据的时候并未对数据进行加锁,事务T在修改数据的时候对数据增加行级共享锁,这种隔离级别会导致脏读
已提交读:事务T在读取数据时增加行级共享锁,读取一旦结束,立即释放;事务T在修改数据时增加行级排它锁,直到事务结束才释放,这种隔离级别解决了脏读
可重复读:事务T在数据读取时,必须增加行级共享锁,直到事务结束;事务T在修改数据过程中,必须增加行级排它锁,直到数据结束;这种隔离级别导致幻读;
序列化:事务T在读取数据时,必须先增加表级共享锁,直到事务结束时才释放;事务T在修改数据时,必须先增加表级排它锁,直到事务结束才释放;
https://www.huaweicloud.com/articles/f6594f3544f516042f08612c5d29a756.html

对于innodb,实现读提交、可重复读是通过快照实现的:
(1)读未提交:select不加锁,可能出现读脏;
(2)读提交(RC):普通select快照读(每句读语句一个快照),锁select /update /delete 会使用记录锁,可能出现不可重复读;
(3)可重复读(RR):普通select快照读(事务开始就一个快照),锁select /update /delete 根据查询条件情况,会选择记录锁,或者间隙锁/临键锁,以防止读取到幻影记录;
(4)串行化:select隐式转化为select … in share mode,会被update与delete互斥;
https://cloud.tencent.com/developer/article/1352976

对隔离级别的介绍:
https://www.jianshu.com/p/05aa6aef105e

redis集群模式

Redis 支持三种集群方案
主从复制模式
Sentinel(哨兵)模式
Cluster 模式
https://segmentfault.com/a/1190000022808576

括号生成(leetcode 22)
二叉树的非递归中序和后序遍历
最长公共子串 (耗时22分钟)十分钟思考 12分钟写
有三个子节点的树的层序遍历,并且实现测试用例编写和测试输出。 这道题半小时写完,既写层序遍历,还需要手写测试用例。
根据前序遍历和中序遍历构造 二叉树
题目是leetcode的中等题73
链表形式的两数求和。LeetCode 445.
leetcode:1038累加树,刚好15号做了
合并两个有序链表
写一个建表语句(table tab,int a,int b)
写一个查询语句,查询出a=1的所有数据
可重复读的原理是什么
最长不含重复字符的子字符串

二叉树最大路径和
LC 179. 最大数
leetcode306累加树
atoi
二分查找左边界

innodb如何实现ACID这四大特性,又遇到了MVCC,orz
分库分表方案,遇到两个的问题(非partion key的查询优化、分库分表后的扩容策略)
redis的一些问题(主从、集群模式,备份方式,底层数据结构)
分布式相关:raft协议

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值