一、用ES构建商品搜索系统
ES本质上是一个支持全文搜索的分布式内存数据库,采用倒排索引,特别适用于构建搜索系统。倒排索引是一种特别为搜索而设计的索引结构,倒排索引先对需要索引的字段进行分词,然后以分词为索引组成一个查找树,这样就把一个全文匹配的查找转换为了对树的查找。这样就可以快速完成查找。缺点是写入和更新性能都比较差,因此倒排索引也只适合全文搜索,不适合更新频繁的交易类数据。
1.1 我们在购物网站搜索商品时,会发现一个搜索提示的功能,比如在某宝输入“牛”的时候,会提示,牛仔裤、牛仔裤女、牛奶等建议的搜素词,那ES是如何实现的呢?
当用户每输入一个字的时候,前端都会把这个字请求后端的搜索推荐接口,所以搜索推荐的请求量远高于搜索框中的搜索。ES针对这种情况提供了suggestion api,并提供了专门的数据结构应对搜索推荐,性能高于match,局限性是只能做前缀匹配。再结合pinyin分词器可以做到输入拼音字母就提示中文。如果想做非前缀匹配,可以考虑Ngram。不过Ngram有些负责,需要开发者自定义分析器。
1.2 缺点
ES有两个问题:1、不适合多索引关联,没有MySQL中的多表关联功能
二、MySQL HA
数据全量备份使用mysqldump命令,但是代价非常大,备份文件包括数据库中的所有数据,占用的磁盘非常大,其次每次拷贝的过程中设计大量数据,会占用数据库大量的CPU、IO等资源,甚至会锁表,会导致数据库的性能严重下降。
数据库增量备份可以利用MySQL的binlog。通过回放binlog,就会把之前对数据库的所有更新操作按照顺序重新执行了一遍。
所以可以通过定期全量备份和binlog,把数据恢复到任何一个时间点。在实际使用中,无论是全量备份还是binlog,都不要和数据库存放在一个服务器上,最好异地备份;另外,在回放binlog的时候,因为回放binlog的操作是具有幂等性的,所以指定的开始时间可以比全量备份的时间稍微提前一点儿,确保全量备份之后的所有操作都在恢复的binlog范围内,这样可以保证恢复的数据的完整性。
2.1 主库同步流程
MySQL主从同步流程:
- 在主库的磁盘中写入binlog;
- 主库更新存储引擎中的数据;
- 给客户端返回成功响应;
- 主库把binlog复制到从库;
- 从库回放binlog,更新存储引擎中的数据。
2.2 HA方案
2.3 主动延迟监控工具
- 如果只是看一下,可以连接到主库上用show slave status命令查看
- 如果需要实时监控主从延迟,可以用pt-heartbeat工具
2.4 性能差异
同步复制时延 = 异步复制的时延 + 最慢的那个从库的复制时延; 理论上来说,同步复制的时延大概是异步复制的2-3倍左右
2.5 注意在回放中的delete操作
三、数据库超时处理
慢SQL很容易导致数据库超时,在系统开发过程中,要考虑两点:
- 在编写SQL的时候,要仔细SQL表的规模、每次遍历的数据量、是否是慢SQL;
- 能不能利用缓存减少数据库查询次数
3.1避免慢SQL
- 用合适的索引,避免全表扫描,毕竟SQL执行速度的快慢关键还是SQL语句扫描数据的行数;
- 排序可能导致慢SQL,以及limit m,n
- 多表联合查询的时候,尽量使用小表驱动大表
- 避免大事务
- 使用SQL执行计划分析
四、使用缓存保护MySQL
cache aside模式:
收到请求之后,先更新数据库,如果更新成功了,再尝试删除缓存;然后再下次查询的时候再加载数据到缓存中。
缺点:在高并发环境下也会出现数据不一致的情况;
解决方案1:数据加版本号,写库时自动增加一。更新缓存时,只允许高版本数据覆盖低版本数据。
解决方案2:
假设a写线程,b读线程:
b线程:读缓存->未命中->上写锁>从db读数据到缓存->释放锁;
a线程:上写锁->写db->删除缓存/改缓存->释放锁;
五、读写分离
MySQL自带主从同步的功能,主要有三种方式分离读写请求:
- 纯手工方式:修改应用程序的DAO层,定义读写两个数据源,指定每一个数据库请求的数据源,可以使用AOP的方式动态切换数据源;
- 组件方式: 例如Sharding-JDBC,将这些组件集成在应用程序中,代理应用程序的所有数据库请求,自动把请求路由到对应数据库实例上;
- 代理方式:在应用程序和数据库实例之间部署一组数据库代理实例,比如说Atlas或者MaxScale。对应用程序来说,数据库代理会把自己伪装成为一个单节点的MySQL实例,应用程序的所有数据库请求被发送给代理,代理分离读写请求,然后转发给对应的数据库实例。
优缺点:第二种读写分离组件方式代理侵入非常少,并且兼顾了性能和稳定性。第三种方式的缺点是增加了系统运行时数据库请求的调用链路,有一定的性能损失,并且代理服务本身也可能出现故障和性能瓶颈等问题,好处是对应用程序完全透明。
如果部署了多个从库,可以使用“HAProxy+Keeplived”来做从库的负载均衡和高可用;另外要注意主从同步延迟造成的数据不一致的问题。
六、主从同步
- MySQL默认采用异步复制的方式
MySQL主库在收到客户端提交事务的请求之后,会先写入binlog,然后再提交事务更新存储引擎中的数据,接着给客户端返回操作成功的响应。同时,从库会有一个专门的复制线程从主库接收binlog,然后把binlog写入到一个中继日志中,再给主库返回复制成功的响应。从库还有另外一个回放binlog的线程,去读取binlog更新存储引擎中的数据。 缺点:提交事务和复制这两个流程分别在不同的线程中执行,互相不会等待,没有办法保证数据能第一时间复制到从库上; - 同步复制 异步复制时,主库提交事务之后,就会给客户端返回响应,而同步复制时,主库在提交事务的时候,会等待数据复制到所有的从库之后,才会给客户端返回响应;
- 半同步复制
- MySQL 5.7版本开始,增加一种半同步复制。异步复制时,事务线程完全不等待复制响应,同步复制时,事务线程要等待所有的复制响应;而半同步复制介于二者之间,事务线程不需要等待所有的复制成功响应,只要一部分复制响应回来之后,就可以给客户端返回了。
七、表里数据越来越多怎么办
- 存档历史数据提升查询性能 如果在迁移的过程中可以停服,最快的方式是重新建立一张新的表,然后把最近三个月的数据复制到新的表中,然后修改表名让新表生效。如果只能在线迁移,那就需要分批迭代删除历史订单数据
八、分库分表
原则: 数据量大,就分表;并发高,就分库。 选择Sharding key的原则就是考虑业务员是如何访问数据的。
- 分片算法
范围分片,容易产生热点问题;
哈希分片算法;
查表表,把分片信息放在一张表里面;