首页 文章 精选 留言 我的

精选列表

搜索[面试],共4912篇文章
优秀的个人博客,低调大师

面试系列七 之 业务交互数据分析

## 6.1 电商常识 `SKU`:一台银色、128G内存的、支持联通网络的iPhoneX `SPU`:iPhoneX `Tm_id`:品牌Id苹果,包括IPHONE,耳机,mac等 ## 6.2 电商业务流程 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2021062616304691.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) ## 6.3 业务表关键字段 ### 6.3.1 订单表(order_info) 标签 |含义 ---|-- id |订单编号 total_amount |订单金额 order_status |订单状态 user_id |用户id payment_way| 支付方式 out_trade_no |支付流水号 create_time |创建时间 operate_time |操作时间 ### 6.3.2 订单详情表(order_detail) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210626163233947.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) ### 6.3.3 商品表 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210626163308927.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) ### 6.3.4 用户表 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210626163342477.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) ### 6.3.5 商品一级分类表 标签| 含义 --|-- id| id name |名称 ### 6.3.6 商品二级分类表 标签 |含义 --|-- id |id name |名称 category1_id |一级品类id ### 6.3.7 商品三级分类表 标签| 含义 --|-- id| id name |名称 Category2_id | 二级品类id ### 6.3.8 支付流水表 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210626163759536.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) **订单表跟订单详情表有什么区别?** - 订单表的订单状态会变化,订单详情表不会,因为没有**订单状态**。 - **订单表**记录user_id,订单id订单编号,订单的总金额order_status,支付方式,订单状态等。 - **订单详情表**记录user_id,商品sku_id ,具体的商品信息(商品名称sku_name,价格order_price,数量sku_num) ## 6.4 MySql中表的分类 **实体表,维度表,事务型事实表,周期性事实表** 其实最终可以把**事务型事实表**,**周期性事实表**统称**实体表**,实体表,维度表统称维度表 订单表(order_info)(周期型事实表) 订单详情表(order_detail)(事务型事实表) 商品表(实体表) 用户表(实体表) 商品一级分类表(维度表) 商品二级分类表(维度表) 商品三级分类表(维度表) 支付流水表(事务型实体表) ## 6.5 同步策略 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210626164305405.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) 实体表,维度表统称维度表,每日全量或者每月(更长时间)全量 事务型事实表:每日增量 周期性事实表:拉链表 ## 6.6 关系型数据库范式理论 `1NF`:**属性不可再分割**(例如不能存在5台电脑的属性,坏处:表都没法用) `2NF`:**不能存在部分函数依赖**(例如主键(学号+课名)-->成绩,姓名,但学号 -->姓名,所以姓名部分依赖于主键(学号+课名),所以要去除,坏处:数据冗余) `3NF`:**不能存在传递函数依赖**(学号 --> 宿舍种类 --> 价钱,坏处:数据冗余和增删异常) **Mysql关系模型**:关系模型主要应用与**OLTP**系统中,为了保证数据的一致性以及避免冗余,所以大部分业务系统的表都是遵循第三范式的。 **Hive 维度模型**:维度模型主要应用于**OLAP**系统中,因为关系模型虽然冗余少, 但是在大规模数据,跨表分析统计查询过程中,会造成多表关联,这会大大降低执行效率。 所以HIVE把相关各种表整理成两种:**事实表和维度表**两种。所有维度表围绕着事实表进行解释。 ## 6.7 数据模型 雪花模型、星型模型和星座模型 (在维度建模的基础上又分为三种模型:星型模型、雪花模型、星座模型。) 星型模型(一级维度表),雪花(多级维度),星座模型(星型模型+多个事实表) ## 6.8 业务数据数仓搭建 sqoop 导数据的原理是mapreduce, import 把数据从关系型数据库 导到 数据仓库,自定义InputFormat, export 把数据从数据仓库 导到 关系型数据库,自定义OutputFormat, 用sqoop从mysql中将八张表的数据导入数仓的ods原始数据层 全量无条件,增量按照创建时间,增量+变化按照创建时间或操作时间。 origin_data sku_info商品表(每日导全量) user_info用户表(每日导全量) base_category1商品一级分类表(每日导全量) base_category2商品二级分类表(每日导全量) base_category3商品三级分类表(每日导全量) order_detail订单详情表(每日导增量) payment_info支付流水表(每日导增量) order_info订单表(每日导增量+变化) ### 6.8.1 ods层 (八张表,表名,字段跟mysql完全相同) 从origin\_data把数据导入到ods层,表名在原表名前加`ods_` ### 6.8.2 dwd层 对ODS层数据进行判空过滤。对商品分类表进行**维度退化**(降维)。其他数据跟ods层一模一样 订单表 `dwd_order_info` 订单详情表 `dwd_order_detail` 用户表 `dwd_user_info` 支付流水表 `dwd_payment_info` 商品表 `dwd_sku_info` 其他表字段不变,唯独商品表,通过关联3张分类表,增加了 ``` category2_id` string COMMENT '2id', `category1_id` string COMMENT '3id', `category3_name` string COMMENT '3', `category2_name` string COMMENT '2', `category1_name` string COMMENT '1', ``` 小结: 1)**维度退化要付出什么代价**?或者说会造成什么样的需求处理不了? - 如果被退化的维度,还有其他业务表使用,退化后处理起来就麻烦些。 - 还有如果要**删除数据,对应的维度可能也会被永久删除。** 2)想想在实际业务中还有**那些维度表可以退化** - 城市的三级分类(省、市、县)等 ### 6.8.3 dws层 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210626165905760.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) 从订单表 `dwd_order_info` 中获取 下单次数 和 下单总金额 从支付流水表 `dwd_payment_info` 中获取 支付次数 和 支付总金额 从事件日志评论表 `dwd_comment_log` 中获取评论次数 最终按照`user_id`聚合,获得明细,跟之前的`mid_id`聚合不同 ## 6.9、需求 ### 6.9.1 需求一:GMV成交总额 从用户行为宽表中`dws_user_action`,根据统计日期分组,聚合,直接sum就可以了。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210626171209574.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) ### 6.9.2、 需求二:转化率 #### 6.9.2.1 新增用户占日活跃用户比率表 从日活跃数表 `ads_uv_count` 和 日新增设备数表 `ads_new_mid_count` 中取即可。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210626171652151.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) #### 6.9.2.2 用户行为转化率表 从用户行为宽表`dws_user_action`中取,**下单人数(只要下单次数>0),支付人数(只要支付次数>0)** 从日活跃数表 `ads_uv_count` 中取活跃人数,然后对应的相除就可以了。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2021062617191173.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) ### 6.9.3、 需求三:品牌复购率 需求:以月为单位统计,购买2次以上商品的用户 #### 6.9.3.1 用户购买商品明细表(宽表) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210626172216239.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) #### 6.9.3.2 品牌复购率表 从用户购买商品明细宽表`dws_sale_detail_daycount`中,根据品牌`id--sku_tm_id`聚合,计算每个品牌购买的总次数,购买人数a=购买次数>=1,两次及以上购买人数b=购买次数>=2,三次及以上购买人数c=购买次数>=3, 单次复购率=b/a,多次复购率=c/a ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210626172304727.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) ## 6.10、 项目中有多少张宽表 宽表要3-5张,用户行为宽表,用户购买商品明细行为宽表,商品宽表,购物车宽表,物流宽表、登录注册、售后等。 **1)为什么要建宽表** 需求目标,把每个用户单日的行为聚合起来组成一张多列宽表,以便之后关联用户维度信息后进行,不同角度的统计分析。 ## 6.11、 拉链表 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210626172911727.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) 订单表拉链表 `dwd_order_info_his` ``` `id` string COMMENT '订单编号', `total_amount` decimal(10,2) COMMENT '订单金额', `order_status` string COMMENT '订单状态', `user_id` string COMMENT '用户id' , `payment_way` string COMMENT '支付方式', `out_trade_no` string COMMENT '支付流水号', `create_time` string COMMENT '创建时间', `operate_time` string COMMENT '操作时间' , `start_date` string COMMENT '有效开始日期', `end_date` string COMMENT '有效结束日期' ``` 1)创建订单表拉链表,字段跟拉链表一样,只增加了有效开始日期和有效结束日期 初始日期,从订单变化表`ods_order_info`导入数据,且让有效开始时间=当前日期,有效结束日期=`9999-99-99` (从mysql导入数仓的时候就只导了新增的和变化的数据`ods_order_info`,`dwd_order_info`跟`ods_order_info`基本一样,只多了一个id的判空处理) 2)建一张拉链临时表`dwd_order_info_his_tmp`,字段跟拉链表完全一致 3)新的拉链表中应该有这几部分数据, - (1)增加订单变化表`dwd_order_info`的全部数据 - (2)更新旧的拉链表左关联订单变化表`dwd_order_info`,关联字段:订单id, where 过滤出`end_date`只等于`9999-99-99`的数据,如果旧的拉链表中的`end_date`不等于`9999-99-99`,说明已经是终态了,不需要再更新 - 如果`dwd_order_info.id is null` , 没关联上,说明数据状态没变,让`end_date`还等于旧的`end_date` - 如果`dwd_order_info.id is not null `, 关联上了,说明数据状态变了,让`end_date`等于当前日期`-1` - 把查询结果插入到拉链临时表中 4)把拉链临时表覆盖到旧的拉链表中 # 关注我的公众号【宝哥大数据】, 更多干货 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210626173520752.png)

优秀的个人博客,低调大师

面试被问MySQL 主从复制,怎么破?

一、前言 随着应用业务数据不断的增大,应用的响应速度不断下降,在检测过程中我们不难发现大多数的请求都是查询操作。 此时,我们可以将数据库扩展成主从复制模式,将读操作和写操作分离开来,多台数据库分摊请求,从而减少单库的访问压力,进而应用得到优化。整理了一份328页MySQLPDF文档 本次测试使用两个虚拟机:ip:192.168.2.21(主) ip:192.168.2.22(从) 二、主从复制原理 同步操作通过 3 个线程实现,其基本步骤如下: 主服务器将数据的更新记录到二进制日志中(记录被称作二进制日志事件)-- 主库线程; 从库将主库的二进制日志复制到本地的中继日志(relay log)-- 从库 I/O 线程; 从库读取中继日志中的事件,将其重放到数据中 -- 从库 SQL 线程。 三、配置主库 # 3.1 创建用户 为了安全起见,准备创建一个新用户用于从库连接主库。 # 创建用户 create user 'repl'@'%' identified by 'repl'; # 授权,只授予复制和客户端访问权限 grant replication slave,replication client on *.* to 'repl'@'%' identified by 'repl'; # 3.2 修改配置文件 1)vim /etc/my.cnf 在[mysqld]下添加: log-bin = mysql-bin log-bin-index = mysql-bin.index binlog_format = mixed server-id = 21 sync-binlog = 1 character-set-server = utf8 2)保存文件并重启主库: service mysqld restart 配置说明: log-bin:设置二进制日志文件的基本名; log-bin-index:设置二进制日志索引文件名; binlog_format:控制二进制日志格式,进而控制了复制类型,三个可选值 -STATEMENT:语句复制 -ROW:行复制 -MIXED:混和复制,默认选项 server-id:服务器设置唯一ID,默认为1,推荐取IP最后部分; sync-binlog:默认为0,为保证不会丢失数据,需设置为1,用于强制每次提交事务时,同步二进制日志到磁盘上。 # 3.3 备份主数据库数据 若主从数据库都是刚刚装好且数据都是一致的,直接执行 show master status 查看日志坐标。 若主库可以停机,则直接拷贝所有数据库文件。 若主库是在线生产库,可采用 mysqldump 备份数据,因为它对所有存储引擎均可使用。 1)为了获取一个一致性的快照,需对所有表设置读锁: flush tables with read lock; 2)获取二进制日志的坐标: show master status; 返回结果: +------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000001 | 120 | | | | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec) 3)备份数据: # 针对事务性引擎 mysqldump -uroot -ptiger --all-database -e --single-transaction --flush-logs --max_allowed_packet=1048576 --net_buffer_length=16384 > /data/all_db.sql # 针对 MyISAM 引擎,或多引擎混合的数据库 mysqldump -uroot --all-database -e -l --flush-logs --max_allowed_packet=1048576 --net_buffer_length=16384 > /data/all_db.sql 1 row in set (0.00 sec) 4)恢复主库的写操作: unlock tables; 四、配置从库 # 4.1 修改配置文件 1)vim /etc/my.cnf 在[mysqld]下添加: log-bin = mysql-bin binlog_format = mixed log-slave-updates = 0 server-id = 22 relay-log = mysql-relay-bin relay-log-index = mysql-relay-bin.index read-only = 1 slave_net_timeout = 10 2)保存文件并重启从库: service mysqld restart 配置说明: log-slave-updates:控制 slave 上的更新是否写入二进制日志,默认为0;若 slave 只作为从服务器,则不必启用;若 slave 作为其他服务器的 master,则需启用,启用时需和 log-bin、binlog-format 一起使用,这样 slave 从主库读取日志并重做,然后记录到自己的二进制日志中; relay-log:设置中继日志文件基本名; relay-log-index:设置中继日志索引文件名; read-only:设置 slave 为只读,但具有super权限的用户仍然可写; slave_net_timeout:设置网络超时时间,即多长时间测试一下主从是否连接,默认为3600秒,即1小时,这个值在生产环境过大,我们将其修改为10秒,即若主从中断10秒,则触发重新连接动作。 # 4.2 导入备份数据 如果 3.3 步骤中没进行备份,忽略此步骤。 mysql -uroot -p < /data/all_db.sql # 4.3 统一二进制日志的坐标 根据 3.3 步骤获取的坐标,统一到从库中: change master to master_host='192.168.2.21', master_user='repl', master_password='repl', master_port=3306, master_log_file='mysql-bin.000001', master_log_pos=120; 注意:此处使用的是新创建的账户。 # 4.4 启动主从复制 1)启动从库 slave 线程: start slave; 2)查看从服务器复制功能状态: show slave status\G; 返回结果: *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.2.21 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000001 Read_Master_Log_Pos: 120 Relay_Log_File: mysql-relay-bin.000002 Relay_Log_Pos: 283 Relay_Master_Log_File: mysql-bin.000001 Slave_IO_Running: Yes Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 120 此处只张贴部分返回结果。 结果说明: Slave_IO_Running:此进程负责 slave 从 master 上读取 binlog 日志,并写入 slave 上的中继日志。 Slave_SQL_Running:此进程负责读取并执行中继日志中的 binlog 日志。 这两个进程的状态需全部为 YES,只要有一个为 NO,则复制就会停止。 当 Relay_Master_Log_File = Master_Log_File 且 Read_Master_Log_Pos = Exec_Master_Log_Pos 时,则表明 slave 和 master 处于完全同步的状态。 五、验证 使用一个简单的例子: 在主库创建名为 mysql_test 的数据库,如果同步成功,那么在从库中也能查询出名为 mysql_test 数据库。 六、参考资料 MySQL 官网整理了一份328页MySQLPDF文档 dev.mysql.com/doc/refman/…

优秀的个人博客,低调大师

面试官: ShardingSphere 学一下吧

文章目录 [toc] 学习之前先了解下分库分表概念:https://spiritmark.blog.csdn.net/article/details/109524713 一、ShardingSphere简介 在数据库设计时候考虑垂直分库和垂直分表。随着数据库数据量增加,不要马上考虑做水平切分,首先考虑缓存处理,读写分离,使 用索引等等方式,如果这些方式不能根本解决问题了,再考虑做水平分库和水平分表。 分库分表导致的问题: 跨节点连接查询问题(分页、排序) 多数据源管理问题 Apache ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 JDBC、 Proxy和 Sidecar(规划中)这 3 款相互独立,却又能够混合部署配合使用的产品组成。 它们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如 Java同构、异构语言、云原生等各种多样化的应用场景。 Apache ShardingSphere定位为关系型数据库中间件,旨在充分合理地在分布式的场 景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库。 它通过关注不变,进而抓住事物本质。关系型数据库当今依然占有巨大市场,是各个公司核心业务的基石,未来也难于撼动,我们目前阶段更加关注在原有基础上的增量,而非颠覆。 二、Sharding-JDBC Sharding-JDBC 是轻量级的 java 框架,是增强版的 JDBC 驱动,简化对分库分表之后数据相关操作。 新建项目并添加依赖: <parent> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-parentartifactId> <version>2.2.1.RELEASEversion> parent> <dependencies> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starterartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-testartifactId> dependency> <dependency> <groupId>com.alibabagroupId> <artifactId>druid-spring-boot-starterartifactId> <version>1.1.20version> dependency> <dependency> <groupId>mysqlgroupId> <artifactId>mysql-connector-javaartifactId> dependency> <dependency> <groupId>org.apache.shardingspheregroupId> <artifactId>sharding-jdbc-spring-boot-starterartifactId> <version>4.0.0-RC1version> dependency> <dependency> <groupId>com.baomidougroupId> <artifactId>mybatis-plus-boot-starterartifactId> <version>3.0.5version> dependency> <dependency> <groupId>org.projectlombokgroupId> <artifactId>lombokartifactId> dependency> dependencies> 2.1 Sharding-JDBC实现水平分表 ① 按照水平分表的方式,创建数据库和数据库表 水平分表规则:如果添加 cid是偶数把数据添加 course_1,如果是奇数添加到 course_2 CREATE TABLE `course_1` ( `cid` bigint(16) NOT NULL, `cname` varchar(255) , `userId` bigint(16), `cstatus` varchar(16) , PRIMARY KEY (`cid`) ) ② 编写实体和 Mapper 类 @Data public class Course { private Long cid; private String cname; private Long userId; private String cstatus; } @Repository public interface CourseMapper extends BaseMapper<Course> { } ③ 详细配置文件 spring: main: allow-bean-definition-overriding: true shardingsphere: datasource: names: m1 m1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.182.200:3306/course_db?serverTimezone=GMT%2B8 username: root password: 1234 sharding: tables: course: actual-data-nodes: m1.course_$->{1..2} key-generator: column: cid type: SNOWFLAKE table-strategy: inline: shardingcolumn: cid algorithm-expression: course_$->{cid%2+1} props: sql: show: true mybatis-plus: configuration: map-underscore-to-camel-case: false ④ 测试 @RunWith(SpringRunner.class) @SpringBootTest public class ShardingSphereTestApplication { @Autowired CourseMapper courseMapper; @Test public void addCourse() { for (int i = 1; i 10; i++) { Course course = new Course(); course.setCname("java" + i); course.setUserId(100L); course.setCstatus("Normal" + i); courseMapper.insert(course); } } @Test public void queryCourse() { QueryWrapper<Course> wrapper = new QueryWrapper<>(); wrapper.eq("cid",493001315358605313L); Course course = courseMapper.selectOne(wrapper); System.out.println(course); } } 2.2 Sharding-JDBC实现水平分库 ① 需求分析 ② 创建数据库和表 ③ 详细配置文件 spring: main: allow-bean-definition-overriding: true shardingsphere: datasource: names: m1,m2 m1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.182.200:3306/course_db_2?serverTimezone=GMT%2B8 username: root password: 1234 m2: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.182.200:3306/course_db_3?serverTimezone=GMT%2B8 username: root password: 1234 sharding: tables: course: actual-data-nodes: m$->{1..2}.course_$->{1..2} key-generator: column: cid type: SNOWFLAKE database-strategy: inline: sharding-column: userId algorithm-expression: m$->{userId%2+1} table-strategy: inline: sharding-column: cid algorithm-expression: course_$->{cid%2+1} props: sql: show: true mybatis-plus: configuration: map-underscore-to-camel-case: false ④ 测试代码 @RunWith(SpringRunner.class) @SpringBootTest public class ShardingSphereTestApplication { @Autowired CourseMapper courseMapper; @Test public void addCourse() { for (int i = 1; i 20; i++) { Course course = new Course(); course.setCname("java" + i); int random = (int) (Math.random() * 10); course.setUserId(100L + random); course.setCstatus("Normal" + i); courseMapper.insert(course); } } @Test public void queryCourse() { QueryWrapper<Course> wrapper = new QueryWrapper<>(); wrapper.eq("cid", 493001315358605313L); Course course = courseMapper.selectOne(wrapper); System.out.println(course); } } 查询实际对应的 SQL: 2.3 Sharding-JDBC操作公共表 公共表 : 存储固定数据的表,表数据很少发生变化,查询时候经常进行关联 在每个数据库中创建出相同结构公共表 ① 思路分析 ② 在对应数据库创建公共表 t_udict&#xFF0C;&#x5E76;&#x521B;&#x5EFA;&#x5BF9;&#x5E94;&#x5B9E;&#x4F53;&#x548C; Mapper`` CREATE TABLE `t_udict` ( `dict_id` bigint(16) NOT NULL, `ustatus` varchar(16) , `uvalue` varchar(255), PRIMARY KEY (`dict_id`) ) ③ 详细配置文件 spring: main: allow-bean-definition-overriding: true shardingsphere: datasource: names: m1,m2 m1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.182.200:3306/course_db_2?serverTimezone=GMT%2B8 username: root password: 1234 m2: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.182.200:3306/course_db_3?serverTimezone=GMT%2B8 username: root password: 1234 sharding: tables: course: actual-data-nodes: m$->{1..2}.course_$->{1..2} key-generator: column: cid type: SNOWFLAKE database-strategy: inline: sharding-column: userId algorithm-expression: m$->{userId%2+1} table-strategy: inline: sharding-column: cid algorithm-expression: course_$->{cid%2+1} t_udict: key-generator: column: dict_id type: SNOWFLAKE broadcast-tables: t_udict props: sql: show: true mybatis-plus: configuration: map-underscore-to-camel-case: false ④ 进行测试 经测试:数据插入时会在每个库的每张表中插入,删除时也会删除所有数据。 @RunWith(SpringRunner.class) @SpringBootTest public class ShardingSphereTestApplication { @Autowired UdictMapper udictMapper; @Test public void addUdict() { Udict udict = new Udict(); udict.setUstatus("a"); udict.setUvalue("已启用"); udictMapper.insert(udict); } @Test public void deleteUdict() { QueryWrapper<Udict> wrapper = new QueryWrapper<>(); wrapper.eq("dict_id", 493080009351626753L); udictMapper.delete(wrapper); } } 2.4 Sharding-JDBC实现读写分离 为了确保数据库产品的稳定性,很多数据库拥有双机热备功能。也就是,第一台数据库服务器是对外提供增删改业务的生产服务器;第二台数据库服务器主要进行读的操作。 Sharding-JDBC通过 sql语句语义分析,实现读写分离过程,不会做数据同步,数据同步通常数据库集群间会自动同步。 详细配置文件: spring: main: allow-bean-definition-overriding: true shardingsphere: datasource: names: m0,s0 m0: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.182.200:3306/course_db?serverTimezone=GMT%2B8 username: root password: 1234 s0: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.182.200:3307/course_db?serverTimezone=GMT%2B8 username: root password: 1234 masterslave: master-data-source-name: m0 slave-data-source-names: s0 props: sql: show: true mybatis-plus: configuration: map-underscore-to-camel-case: false 经过测试:增删改操作都是会通过 master数据库,同时 master数据库会同步数据给 slave数据库;查操作都是通过 slave数据库. 三、Sharding-Proxy Sharding-Proxy定位为 透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持, 目前仅 MySQL和 PostgreSQL版本。 Sharding-Proxy是独立应用,需要安装服务,进行分库分表或者读写分离配置,启动使用。 <br> Sharding-proxy的使用参考:Sharding-Proxy的基本使用。 微信搜一搜:全栈小刘

优秀的个人博客,低调大师

面试命中率90%的点 —— MySQL锁

一、对MySQL的锁的了解 当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。 就像酒店的房间,如果大家随意进出,就会出现多人抢夺同一个房间的情况,而在房间上装上锁,申请到钥匙的人才可以入住并且将房间锁起来,其他人只有等他使用完毕才可以再次使用。 二、隔离级别与锁的关系 在Read Uncommitted级别下,读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突 在Read Committed级别下,读操作需要加共享锁,但是在语句执行完以后释放共享锁。 在Repeatable Read级别下,读操作需要加共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁。 SERIALIZABLE 是限制性最强的隔离级别,因为该级别锁定整个范围的键,并一直持有锁,直到事务完成。 三、按照锁的粒度分数据库锁有哪些?锁机制与InnoDB锁算法 在关系型数据库中,可以按照锁的粒度把数据库锁分为行级锁(INNODB引擎)、表级锁(MYISAM引擎)和页级锁(BDB引擎 )。 MyISAM和InnoDB存储引擎使用的锁: MyISAM采用表级锁(table-level locking)。 InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁。 行级锁,表级锁和页级锁对比 行级锁:MySQL中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁和排他锁。 特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 表级锁:MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MyISAM与InnoDB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。 特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。 页级锁:是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。 特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般 四、从锁的类别上分MySQL都有哪些锁呢?像上面那样子进行锁定岂不是有点阻碍并发效率了 从锁的类别上来讲,有共享锁和排他锁。 共享锁: 又叫做读锁。当用户要进行数据的读取时,对数据加上共享锁。共享锁可以同时加上多个。 排他锁: 又叫做写锁,当用户要进行数据的写入时,对数据加上排他锁。排他锁只可以加一个,他和其他的排他锁,共享锁都相斥。 用上面的例子来说就是用户的行为有两种,一种是来看房,多个用户一起看房是可以接受的。一种是真正的入住一晚,在这期间,无论是想入住的还是想看房的都不可以。 锁的粒度取决于具体的存储引擎,InnoDB实现了行级锁,页级锁,表级锁。 他们的加锁开销从大到小,并发能力也是从大到小。 五、MySQL中InnoDB引擎的行锁是怎么实现的? InnoDB是基于索引来完成行锁 例: select * from tab_with_index where id = 1 for update; for update 可以根据条件来完成行锁锁定,并且 ID 是有索引键的列,如果 ID不是索引键那么InnoDB将完成表锁,并发将无从谈起 六、InnoDB存储引擎的锁的算法有三种 1.Record lock:单个行记录上的锁 2.Gap lock:间隙锁,锁定一个范围,不包括记录本身 3.Next-key lock:record+gap 锁定一个范围,包含记录本身 七、相关知识点: Innodb对于行的查询使用next-key lock Next-locking keying为了解决Phantom Problem幻读问题 当查询的索引含有唯一属性时,将next-key lock降级为record key Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1 八、什么是死锁?怎么解决? 死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。 常见的解决死锁的方法 1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。 2、在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率; 3、对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率; 如果业务处理不好可以用分布式事务锁或者使用乐观锁 九、数据库的乐观锁和悲观锁是什么?怎么实现的? 数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来,直到提交事务。 实现方式:使用数据库中的锁机制 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。在修改数据的时候把事务锁起来,通过Version的方式来进行锁定。 实现方式:一般会使用版本号机制或CAS算法实现。 两种锁的使用场景 从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。 但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行Retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。 最后 感谢大家看到这里,文章有不足,欢迎大家指出;如果你觉得写得不错,那就给我一个赞吧。

优秀的个人博客,低调大师

那些年,面试官问你的消息队列

MQ理论介绍 一、为什么需要消息队列(MQ) 主要原因是由于在高并发环境下,同步请求来不及处理,请求往往会发生阻塞。大量的请求到达访问数据库,导致行锁表锁,最后请求线程会堆积过多,从而触发 too many connection错误,引发雪崩效应。我们使用消息队列,通过异步处理请求,从而缓解系统的压力。核心:异步处理、流量削峰、应用解耦 二、应用场景 异步处理,流量削峰,应用解耦,消息通讯四个场景 2.1、异步处理 场景1:用户注册后,需要发送注册邮件和注册短信。 串行方式:将注册信息写入 数据库 成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端 并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间 假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。 因为CPU在单位时间内处理的请求数是一定的,假设CPU在1秒内吞吐量是100次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。 并行方式处理的请求量是10次(1000/100) 小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题? 引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下: 按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回, 因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。 比串行提高了3倍,比并行提高了2倍。 场景2:订单处理,前端应用将订单信息放到队列,后端应用从队列里依次获得消息处理,高峰时的大量订单可以积压在队列里慢慢处理掉。 2.2、流量削峰 流量削峰也是消息队列中的常用场景,一般在 秒杀或团抢活动 中使用广泛 应用场景:秒杀活动,一般会因为流量过大,导致流量报增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。 可以控制活动的人数 可以缓解短时间内高流量压垮应用 让消息不直接到达服务器,而是先让 「消息队列」 保存这些数据,然后让下面的服务器每一次都取各自能处理的请求数再去处理,这样当请求数超过服务器最大负载时,就不至于把服务器搞挂了。 秒杀业务根据消息队列中的请求信息,再做后续处理 2.3、应用解耦 场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用系统库存接口。 传统模式的缺点: 假如库存系统无法访问,则订单减库存将失败,从而导致订单失败 解决订单系统与库存系统耦合,如何解决? 引入消息队列后的方案: 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功 库存系统:订阅下单的消息,采用pub/sub(发布/订阅)的方式,获取下单信息,库存系统根据下单信息,进行库存操作 假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入信息队列就不再关心其他后续操作了。实现订单系统与库存系统的应用解耦。 2.4、日志处理 日志处理是将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题。架构简化如下 日志采集客户端,负载日志数据采集,定时写入Kafka队列 Kafka消息队列,负载日志数据的接收,储存和转发 日志处理应用:订阅并消费Kafka队列中的日志数据 (1) Kafka:接收用户日志的消息队列 (2) Logstash:做日志解析,统一成JSON 输出给Elasticsearch (3)Kibana:基于Elasticsearch 的数据可视化组件,超强的数据可视化能力是众多公司选择Elkstack的重要原因 2.5、消息通讯 消息通讯是指,消息队列一般都内置了高效的通信机制就,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。 点对点通讯: 客户端A和客户端B使用同一队列,进行消息通讯。 聊天室通讯: 客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现类似聊天室效果。以上实际是消息队列的两种消息模式,点对点或发布订阅模式。 3、衡量指标 我们从服务性能、数据存储、集群结构三个方面去对比,选择适合自己项目的消息中间件 特性 ActiveMQ RabbitMQ RocketMQ Kafka 单机吞吐量 万级 万级 十万级 十万级 topic 数量对吞吐量的影响 - - topic 可以达到几百,几千个的级别,吞吐量会有较小幅度的下降 topic 从几十个到几百个的时候,吞吐量会大幅度下降 时效性 毫秒级 微妙级 毫秒级 毫秒级 可用性 高 高 非常高,分布式架构 非常高,分布式架构 消息可靠性 有较低的概率丢失数据 - 经过参数优化配置,可以做到 0 丢失 经过参数优化配置,消息可以做到 0 丢失 功能支持 完善 并发能力很强,性能极其好,延时很低 MQ 功能较为完善,还是分布式的,扩展性好 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准 优劣势总结 非常成熟,功能强大;偶尔会有较低概率丢失消息;社区不活跃了 性能极其好,延时很低;功能完善;提供管理界面;社区比较活跃;吞吐量较低;使用 erlang 开发源码阅读不方便; 接口简单易用;吞吐量高;分布式扩展方便;社区还算活跃;经过双 11 的考验 MQ 功能比较少;吞吐量高;分布式架构;可能存在消息重复消费问题;主要适用大数据实时计算以及日志收集; 4、AvctiveMQ和RabbitMQ 4.1、ActiveMQ 特点: 早期主流的消息中间件,包括ZeroMQ,但是这几年使用的很少了,主要在长期维护的项目中使用。API丰富,本身很成熟但是在高并发、大数据 环境下的性能不够出色,主要试用于中小型项目,有较低的概率丢失数据,最主要是的,官方现在维护的频率一直在降低,好几个月才发布一个版本。 架构: 1、Master-Slave模式,通过Zookeeper实现节点切换和故障转移 2、NetWork模式,相当于多套Master-Slave模式,通过NetWork网关进行集成 4.2、RabbitMQ RabbitMQ 是使用 Erlang 语言开发的开源消息队列系统。基于AMQP协议来实现的。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布与订阅)、可靠性、安全。 AMQP协议:更多的用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次场景。 好处在于可以支撑高并发、高吞吐、性能很高,同时有非常完善便捷的后台管理界面可以使用。另外,他还支持集群化、高可用部署架构、消息高可靠支持,功能较为完善。

优秀的个人博客,低调大师

8道Python基础面试练习题

1.26个字母大小写成对打印,例如:Aa,Bb...... for i in range(26): print(chr(65+i)+chr(97+i)) 2.一个list包含10个数字,然后生成一个新的list,要求新的list里面的数都比之前的数多1 list=[2,3,6,4,7,5,1,8,9,0] list1=[] for i in list: list1.append(i+1) print(list1) 3.倒序取出每个单词的第一个字母,例如:I am a good boy!方法1 tre='I am a good boy!' t=tre.split() #print(t) t.reverse() list=[] #print(t) for i in t: list.append(i[0]) print(list) 方法2 a = "I AM A BOY" result = [] for i in a.split()[::-1]: result.append(i[0]) print(result) 4.输入一个自己的生日月份,用if和else判断一下当月是不是你的生日月第一种方法,datetime模块获取时间 import datetime date=datetime.datetime.now() #获取当前时间 # print(date.strftime('%Y-%m-%d')) #把当前时间格式化为可读懂的年月日 r=date.strftime('%m') #把当前时间格式化为可读懂的年月日,只取月份 print(r) #Python学习交流QQ群:579817333 t=input('请输入自己的生日月份:') if t==r: print('true') else: print('不是') 第二种方法,time模块获取时间 import time # date=time.time() #获取当前时间 # print(date) # print(time.localtime(time.time()))#按固定格式显示当前时间 # print(time.strftime('%Y-%m-%d')) #把当前时间格式化为可读懂的年月日 # print(time.strftime('%Y-%m-%d',time.localtime(time.time()))) #把时间格式化为可读懂的年月日,后一个参数可省略 # print(time.strftime('%m',time.localtime(time.time()))) #只取月儿份 #t=time.strftime('%m',time.localtime(time.time()))#只取月儿份 t=time.strftime('%m')#只取月儿份 print(t)#Python学习交流QQ群:579817333 r=input('请输入自己的生日月份:') if t==r: print('true') else: print('不是') 5.输入3个字母:e、a、r,如果输入e,那么推出循环,如果输入a,执行continue,如果输入r,那么再读取一次字母,并打印,用死循环实现。 while True: str = input('请输入三个字母:') if str=='r': print(str) if str=='a': continue if str=='e': break else: print('输入有误') 6.输入3个字母:e、a、r,如果输入e,那么退出循环,如果输入a,执行continue,如果输入r,那么再读取一次字母,并打印,只允许输入三次字母,重复输入的字母不算在内。 count = 0 for i in range(3): letter = input("send a letter%d:"%i) if letter == 'e': break elif letter == 'a': continue elif letter == 'r': count += 1 if count == 2: input("send a letter dddd:") 7.把一个字符串"abcdefg"插入到一个list中,每个字母占一个list中的元素位置,例如: ["a","b","c","d","e","f","g"] ls=["a","b","c","d","e","f","g"] s="abcdefg" lt=[] #插入元素到后边 for i in s : ls.extend(i) print(ls) for i in s: ls.append(i) print(ls) #每个元素都插在第一个,或者说倒序插入列表前边 for i in s: ls.insert(0,i) print(ls) 8.['a','b','c','d','e','f','g']操作这个list,拼出一个字符串"adg" lis=['a','b','c','d','e','f','g'] print(len(lis)) t=lis[0]+lis[int(len(lis)/2)]+lis[-1] print(t) 或 print("".join(lis[::3]))

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。