卡片工作法在notion中的实践

概念 什么是卡片工作法 实践 选用工具-以notion为例 notion synced block支持的功能 写在一个地方,可以同步到任何地方 在使用同步块的任何页面,随时编辑,可以实时同步,不用找到最原始的出处,方便修改 任意block可以随意转换成同步块 在某些场景下,一个同步块的内容在不同的使用场景下,需要进行些许的修改,但是又不想影响到原始的内容,则可以在使用的地方对这个同步块进行unsync操作 支持源同步块一键取消所有同步→unsync Notion中如何实践卡片笔记 参考 https://www.bilibili.com/video/BV1mG411g7Wz 让AI助力资讯收集 参考 https://www.bilibili.com/video/BV1524y1M73Y, 新建一个带有AI block的模板, 让AI来帮助我们将文章分类, 以及做摘要, 方便分类 思考 参考 Synced blocks - Notion Help Center 【社会学】 访谈卢曼:1973年与1989年 (双语)_哔哩哔哩_bilibili 创意笔记大师:卢曼和达芬奇 - Forte Labs 卢曼纪录片卡片盒分析笔记 关联起发散的思维成果,我用 Notion 实践卡片盒笔记法 https://www.bilibili.com/video/BV1524y1M73Y

January 28, 2026 · 1 min · 43 words · Jimmy

20200822-杭州永辉菜价

这些物价正常吗

August 22, 2022 · 1 min · 43 words · Jimmy

机器学习实战-学习笔记之Decision Trees

项目地址:http://github.com/jimersylee/MachineLearningAction 决策树(Decision Trees) 类似20个问题的游戏 参与游戏的一方在脑海里想某个事物,其他参与者向他提问题,只允许提20个问题,问题的答案只能用对或错回答,问问题的人通过推断分解,逐步缩小带猜测事物的范围.决策树的工作原理与20个问题类似,用户输入一系列数据,然后给出游戏的答案 决策树 优点:计算复杂度不高,输入结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据 缺点:可能产生过度匹配问题 适用数据类型:数值型与标称型 决策树的一般流程 收集数据:可以适用任何方法 准备数据:树构造算法只适用于标称型数据,因此数值型数据必须离散化 分析数据:可以适用任何方法,构造书完成之后,我们应该检查图形是否符合预期 训练算法:构造树的数据结构 测试算法:适用经验树计算错误率 适用算法:此步骤可以适用于任何监督学习算法,而适用决策树可以更好地理解数据的内在含义 # coding:utf-8from mathimport log import operator import matplotlib.pyplotas plt defcalcShannonEnt(dataSet): """ 计算给定数据集的香农熵,香农熵代表数据的无序程度,香农熵越大,数据集越无序 :param dataSet: 数据集 :return:香农熵的值 """ numEntries = len(dataSet)# 获得数据中实例的总数 labelCounts = {}# 声明一个字典# 为所有可能分类创建字典,它的键值是最后一列的数值for featVecin dataSet: currentLabel = featVec[-1]# 目前的标签=数据集的每行的最后一个元素if currentLabelnotin labelCounts.keys():# 如果键值不存在,则扩展字典并将当前键值加入字典 labelCounts[currentLabel] = 0# 初始化为0 labelCounts[currentLabel] = labelCounts[currentLabel] + 1# 每个键值都记录了当前类别出现的次数,出现一次加1 shannonEnt = 0.0 for keyin labelCounts: prob = float(labelCounts[key]) / numEntries# 使用所有类标签的发生频率计算类别出现的概率 shannonEnt = shannonEnt - prob * log(prob, 2)# 使用这个概率计算香农熵 以2为底求对数return shannonEnt defcreateDataSet(): """ 创建简单鱼类鉴定数据集 :return:dataSet(数据集),labels(标签) """ _dataSet = [ [1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no'] ] _labels = ['no surfacing', 'flippers'] return _dataSet, _labels myData, labels = createDataSet() print myData print calcShannonEnt(myData) """ result [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']] 0.970950594455 """ # 熵越高,则混合的数据也越多,我们可以在数据集中添加更多的分类,观察熵是如何变化的,这里我们增加第三个名为maybe的分类,测试熵的变化 myData[0][-1] = 'maybe'# 第0行的最后一个元素置为maybeprint myData print calcShannonEnt(myData) """ [[1, 1, 'maybe'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']] 1.37095059445 """ # 得到熵以后,我们就可以按照获取最大信息增益的方法划分数据集# 另一个度量集合无需程度的方法是`基尼不纯度`(Gini impurity),简单地说就是从一个数据集中随机选取子项,度量其被错误分类到其他分组里的概率""" 分类算法除了要测量信息熵,还需要划分数据集,度量花费数据集的熵,以便判断当前是否正确划分了数据集,我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照那个特征划分数据集是最好的划分方式 """ defsplitDataSet(dataSet, axis, value): """ 按照给定特征划分数据集 :param dataSet: 待划分的数据集 :param axis:划分数据集的特征位 :param value:预设特征值 :return: """ retDataSet = []# 创建新的list对象,因为函数中传递的是列表的引用,在函数内部对列表对象的修改,将会影响该列表对象的整个生存周期,因此需要创建一个新的列表for featVecin dataSet: if featVec[axis] == value:# 如果axis位的值等于预设的特征值 reducedFeatVec = featVec[:axis]# 取行数据的前axis位 reducedFeatVec.extend(featVec[axis + 1:])# 加上axis位后的元素 retDataSet.append(reducedFeatVec)# 就是将除了axis特征位的元素抽取出来return retDataSet # 测试splitDataSetprint "测试splitDataSet" myData, labels = createDataSet() print myData newData = splitDataSet(myData, 0, 1)# 将待测试数据的第0位为1的数据的其他特征和标签筛选出来print newData defchooseBestFeatureToSplit(dataSet): """ 选择最好的数据集划分方式 :param dataSet: 数据集 :return: """ numFeatures = len(dataSet[0]) - 1 # 计算整个数据的原始香农熵 baseEntropy = calcShannonEnt(dataSet) bestInfoGain = 0.0 bestFeature = -1 # 遍历数据集中的所有特征for iin range(numFeatures): # 创建唯一的分类标签列表 featList = [example[i]for examplein dataSet] uniqueVals = set(featList)# 将list转换为set,集合类型中每个值不同,从列表中创建集合是Python语言中得到列表中唯一元素值的最快方法 newEntropy = 0.0 # 计算每种划分方式的信息熵for valuein uniqueVals:# 遍历当前特征中的所以唯一属性值,对每个特征划分一次数据集 subDataSet = splitDataSet(dataSet, i, value) prob = len(subDataSet) / float(len(dataSet)) newEntropy += prob * calcShannonEnt(subDataSet)# 并对所有唯一特征值得到的熵求和 infoGian = baseEntropy - newEntropy# 信息增益=熵的减少值或者数据无序度的减少# 计算最好的信息增益if infoGian > bestInfoGain: bestInfoGain = infoGian bestFeature = i return bestFeature# 返回最好特征划分的索引值# 获取最好特征划分索引值print "获取最好特征划分索引值" myData, labels = createDataSet() print "dataSet:" + str(myData) print "best index:" + str(chooseBestFeatureToSplit(myData)) """ 代码表示,第0个特征是最好的用户划分数据集的特征 """ """ 递归构建决策树 """ defmajorityCnt(classList): """ 类似classify0部分的投票表决 创建键值为classList中唯一值的数据字典,字典对象存储了classList中每个类标签出现的频率,最后利用operator操作键值排序字典,返回出现次数最多的分类名称 :rtype: str :param classList: 分类名称的列表 :return: 返回出现次数最多的分类名称 """ classCount = {} for votein classList: if votenotin classCount.keys():# 如果投票的类别不在classCount字典的key中, classCount[vote] = 0# 则新建此key,value设置为0 classCount[vote] += 1 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0] defcreateTree(dataSet, labels): """ 创建树,使用了递归 :param dataSet:数据集 :param labels: 分类标签 :return: 树结构 """ classList = [example[-1]for examplein dataSet]# 将数据集中的每一行的最后一个元素的值取出生成类别列表classListif classList.count(classList[0]) == len(classList):# 递归停止条件1return classList[0]# 类别完全相同时停止继续划分if len(dataSet[0]) == 1:# 递归停止条件2return majorityCnt(classList)# 遍历完所有特征时返回出现次数最多的 bestFeat = chooseBestFeatureToSplit(dataSet)# 选择最好的划分特征位 bestFeatLabel = labels[bestFeat]# 取出最好的标签 myTree = {bestFeatLabel: {}}# 初始化一个tree字典del (labels[bestFeat])# 删除最好的标签,防止树的下一个节点还使用此标签 featValues = [example[bestFeat]for examplein dataSet]# 创建最好的特征位的值所构建的list uniqueVals = set(featValues)# 生成唯一的特征位的值构成的集合setfor valuein uniqueVals: subLabels = labels[:]# 复制所有标签,且树不会和已经存在的标签搞混 myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)# 递归创建树return myTree # 构建树测试print "构建树测试" myData, labels = createDataSet() myTree = createTree(myData, labels) print myTree # coding:utf-8import matplotlib.pyplotas plt# 导入绘制图库""" 绘制树图 """ decisionNode = dict(boxstyle="sawtooth", fc="0.8") leafNode = dict(boxstyle="round4", fc="0.8") arrow_args = dict(arrowstyle="<-") defgetNumLeafs(myTree): """ 获取叶节点的数目 :param myTree:树结构 :return: 叶节点的数目 """ numLeafs = 0 firstStr = myTree.keys()[0] secondDict = myTree[firstStr] for keyin secondDict.keys(): if type(secondDict[ key]).__name__ == 'dict':# test to see if the nodes are dictonaires, if not they are leaf nodes numLeafs += getNumLeafs(secondDict[key]) else: numLeafs += 1 return numLeafs defgetTreeDepth(myTree): """ 获取树的层数(深度) :param myTree: :return: 树的层数(深度) """ maxDepth = 0 firstStr = myTree.keys()[0] secondDict = myTree[firstStr] for keyin secondDict.keys(): if type(secondDict[ key]).__name__ == 'dict':# 判断此节点是否是字典,如果不是就是叶节点 thisDepth = 1 + getTreeDepth(secondDict[key])# 是字典,层数+1else: thisDepth = 1# 叶节点,层数为1if thisDepth > maxDepth: maxDepth = thisDepth return maxDepth defplotNode(nodeTxt, centerPt, parentPt, nodeType): """ 绘制节点 :param nodeTxt:节点名称 :param centerPt: 目前的位置 :param parentPt: 父节点的位置 :param nodeType: 节点类型 :return: """ createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction', xytext=centerPt, textcoords='axes fraction', va="center", ha="center", bbox=nodeType, arrowprops=arrow_args) defplotMidText(currentPt, parentPt, txtString): """ 绘制中间的文本 :param currentPt:目前的位置 :param parentPt: 父节点的位置 :param txtString: 想绘制的文本 :return: """ xMid = (parentPt[0] - currentPt[0]) / 2.0 + currentPt[0] yMid = (parentPt[1] - currentPt[1]) / 2.0 + currentPt[1] createPlot.ax1.text(xMid, yMid, txtString, va="center", ha="center", rotation=30) defplotTree(myTree, parentPt, nodeTxt):# if the first key tells you what feat was split on numLeafs = getNumLeafs(myTree)# this determines the x width of this tree depth = getTreeDepth(myTree) firstStr = myTree.keys()[0]# the text label for this node should be this cntrPt = (plotTree.xOff + (1.0 + float(numLeafs)) / 2.0 / plotTree.totalW, plotTree.yOff) plotMidText(cntrPt, parentPt, nodeTxt) plotNode(firstStr, cntrPt, parentPt, decisionNode) secondDict = myTree[firstStr] plotTree.yOff = plotTree.yOff - 1.0 / plotTree.totalD for keyin secondDict.keys(): if type(secondDict[ key]).__name__ == 'dict':# test to see if the nodes are dictonaires, if not they are leaf nodes plotTree(secondDict[key], cntrPt, str(key))# recursionelse:# it's a leaf node print the leaf node plotTree.xOff = plotTree.xOff + 1.0 / plotTree.totalW plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode) plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key)) plotTree.yOff = plotTree.yOff + 1.0 / plotTree.totalD # if you do get a dictionary you know it's a tree, and the first element will be another dictdefcreatePlot(inTree): fig = plt.figure(1, facecolor='white') fig.clf() axprops = dict(xticks=[], yticks=[]) createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)# no ticks# createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses plotTree.totalW = float(getNumLeafs(inTree)) plotTree.totalD = float(getTreeDepth(inTree)) plotTree.xOff = -0.5 / plotTree.totalW plotTree.yOff = 1.0 plotTree(inTree, (0.5, 1.0), '') plt.show() # def createPlot():# fig = plt.figure(1, facecolor='white')# fig.clf()# createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses# plotNode('a decision node', (0.5, 0.1), (0.1, 0.5), decisionNode)# plotNode('a leaf node', (0.8, 0.1), (0.3, 0.8), leafNode)# plt.show()defretrieveTree(i): listOfTrees = [{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}, {'no surfacing': {0: 'no', 1: {'flippers': {0: {'head': {0: 'no', 1: 'yes'}}, 1: 'no'}}}} ] return listOfTrees[i] # createPlot(thisTree) myTree = retrieveTree(0) createPlot(myTree)

July 21, 2022 · 5 min · 885 words · Jimmy

Yii1.x的管理后台分页导致的性能问题

Yii 1.x的管理后台分页导致的性能问题 上午客服反应管理后台在售管理和求购管理打开缓慢,我跟踪了下原因 我猜想原因如下: 交易相关的,走了交易服务的接口,接口比较缓慢 没加limit或者查询条件比较复杂没走索引 查看代码,发现是php代码直接查询数据库的,排除第一个原因,接下来往直接查数据的方向查,可能原因有 交易数据负载比较高,查询响应慢 管理后台使用了外网地址连接数据库 查看交易数据的近期慢SQL,是否有管理后台请求的慢SQL,发现端倪 ![page1.png](2-Areas/blog/public-blog/hugo/content/posts/Yii%201%20x的管理后台分页导致的性能问题/page1.png) 本地开启浏览器显示sql调试信息 发现会select count(*) from trade_product_sell,这个应该是为了分页,但是线上表数据已经5600万了,这个sql得执行9秒,能不慢吗 管理后台的分页插件都会执行这个count操作 public function getTotalItemCount($refresh=false) { if($this->_totalItemCount===null || $refresh) $this->_totalItemCount=$this->calculateTotalItemCount(); return $this->_totalItemCount; } 找到问题后,怎么处理,现状如下 此处代码为yii1.x的分页逻辑,查询总条数,然后计算分页,此代码在管理后台使用的非常多 此分页逻辑在针对小表查询的时候一点问题也没有,且使用比较方便 原则是不能改框架,且一定要获取到总条数,前端页面才能正常显示分页 解决方案 既然总条数的获取不可避免,那就优化count(*)的性能 根治的方法:针对大表进行归档处理,这个是交易系统的表,处理起来需要时间,可能要排到下季度了 治标的方法: 赋值一个假的总条数,这个的问题是体验不好,有的表只有几行,却显示一堆分页 治标的方法: 将查询总数缓存起来,虽然第一次会慢,但是之后都挺快的 代码实现 不能修改框架代码,防止之后升级框架版本的时候覆盖了 可以选择修改基础的模型类,增加一个方法,进行总数缓存,且可以按需使用,针对大表及对总数精确度不敏感的场景 BasicAcitveRecord类中增加方法/** * 缓存一下分页组件使用的数据总条数 * 适用场景: * 对count不要求精确的,表比较大的,select count(*) 比较慢的那种,如果不缓存,每次都得查count(*)比较慢 * 不适用场景: * 对总数要求精确的 * 实现原理,使用表名+查询条件的md5作为key,缓存此条件下的查询总条数 * @param CActiveDataProvider $dataProvider :分页使用的dataProvider */ public function cacheTotalCount(CActiveDataProvider $dataProvider) { /** * @var $redis ARedisCache */ $redis = Yii::app()->cache; $md5 = md5(json_encode($dataProvider->getCountCriteria()) . $dataProvider->model->tableName()); $count = $redis->get(RedisKeyService::getCachedTabelCountKey($md5)); if ($count != null) { $dataProvider->setTotalItemCount($count); } else { $redis->set(RedisKeyService::getCachedTabelCountKey($md5), $dataProvider->getTotalItemCount(), 3600); } } ...

September 10, 2021 · 1 min · 130 words · Jimmy

服务重新发布导致的mysql会话飙升以及网站访问延迟的问题排查处理

故障表现 在重新发布交易服务的时候,网站出现访问延迟增长的情况 故障分析 排查清单 RDS数据库指标是否正常,CPU,内存正常,会话数激增 ES搜索服务是否正常,正常 REDIS服务是否正常,正常 确认基本方向,可能数据库连接配置存在问题 排查druid依赖版本问题 查看pom依赖,发现版本太老了,1.0.16的版本发布于2015年10月 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.16</version> </dependency> 进行升级测试,保险起见,升级到1.1.x系列的最新版本,发布于2020.9月,还有最新的1.2.x版本可能存在较大更新,没有尝试 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.24</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.24</version> </dependency> 本地测试正常,无兼容性修改 排查druid配置问题 生产环境配置如下 trade: datasource: #druid相关配置 druid: #监控统计拦截的filters filters: stat driverClassName: com.mysql.jdbc.Driver #配置基本属性 url: **** username: *** password: **** #配置初始化大小/最小/最大 initialSize: 50 minIdle: 10 maxActive: 300 #获取连接等待超时时间 maxWait: 60000 #间隔多久进行一次检测,检测需要关闭的空闲连接 timeBetweenEvictionRunsMillis: 60000 #一个连接在池中最小生存的时间 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 'x' testWhileIdle: true testOnBorrow: false testOnReturn: false #打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false poolPreparedStatements: false maxPoolPreparedStatementPerConnectionSize: 20 查询druid配置最佳实践相关资料 有赞DB连接池性能优化-InfoQ ...

July 24, 2021 · 4 min · 797 words · Jimmy

使用nvm管理node环境

archlinux,manjaro 使用nvm安装管理node # 安装nvm sudo pacman -S nvm #配置nvm echo "source /usr/share/nvm/init-nvm.sh">>~/.profile #立即生效 source /usr/share/nvm/init-nvm.sh # 安装稳定版 nvm install stable # 或者安装指定版本 nvm install 10.15.0 # 指定使用某个版本 nvm use 10.15.0 # 检查版本是否正确 node -v https://github.com/creationix/nvm

June 23, 2021 · 1 min · 35 words · Jimmy

分布式ID重复问题

背景 线上存在数据表主键重复的错误 通过阿里云日志搜索以下关键词可以查询到 “Duplicate” and “primary” 日志例子 log:2021-04-22 13:19:31,896 ERROR [http-nio2-8088-exec-100] [ac140db2-knscphtx-437669] [steam-trade-boot] c.x.s.t.s.b.i.TradeOrderLogServiceImpl - 插入日志冲突 org.springframework.dao.DuplicateKeyException: ### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '859225024379092992' for key 'PRIMARY' ### The error may exist in com/xingchao/steam/trade/dal/mapper/TradeOrderLogMapper.java (best guess) ### The error may involve com.xingchao.steam.trade.dal.mapper.TradeOrderLogMapper.insertSelective-Inline ### The error occurred while setting parameters ### SQL: INSERT INTO trade_order_log ( id,order_asset_id,type,before_status,after_status,event,content,create_time,update_time ) VALUES( ?,?,?,?,?,?,?,?,? ) ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '859225024379092992' for key 'PRIMARY' 代码例子 //项目中所有主键使用的SnowFlakeUtil这个组件生成id logDO.setId(SnowFlakeUtil.getId()); tradeOrderLogMapper.insertSelective(logDO); 表现 针对这种频繁插入的情况,容易出现id重复的问题,与当初预想的不一样 ...

April 16, 2021 · 2 min · 372 words · Jimmy

跨数据库实例以及复杂聚合查询数据分析方案调研

背景 目前BI需求繁多,且都是些复杂联表查询,很容易产生数据库性能问题,影响线上用户, 目前架构如下,虽然能够满足现在的需求,但面对未来更大的数据量,更加复杂的统计需求,将只能通过添加只读实例和升级配置解决 用户基础信息与交易信息不在同一个实例,数据分析需要联合表查询得到数据,目前先查用户id再去用户信息表取存在诸多不便,需要寻求更方便的形式 针对用户行为漏斗分析等场景,MySQL的支持很差,基本查不动,需要更合适的数据库 需求 支持更复杂的聚合查询,更快的查询速度 不影响用户体验 更少的成本 方案 方案A:metabase,使用matebase构建目前的bi.xxx.com,数据源自RDS,当进行复杂查询的时候存在性能问题,会影响生产环境 初代版本 演进版本 备选方案 B:使用阿里云DLA服务 阿里云DLA ,介绍 https://help.aliyun.com/document_detail/70378.html 跨库查询支持 https://help.aliyun.com/document_detail/107698.html 知乎专栏:https://www.zhihu.com/column/data-lake-analytics 测试过程 1:尝试云原生数据湖的联合查询多个MySql实例,通过https://help.aliyun.com/document_detail/107698.html?spm=a2c4g.11186623.6.837.4d7974f4GsP1S0 可以实现不同数据库的多表联合查询,但是咨询工单反馈,查询过程仍再原数据库中执行,在小数量的数据库查询中影响不大,但是在查询量较大较频繁时,无法 保证原数据库的性能。于是尝试其他方法。 2:尝试创建云原生数据湖分析中的T+1多库合并建仓,将数据库导入到DLA数据湖中,通过自己新创建的数据量很小的数据库导入,可以实现,并且不会占用大量性能。 但针对test库的多库合并建仓时,因为性能原因造成较大的影响。并且询问阿里云工单之后反馈,多库合并建仓再第一次入湖之后,再每一次的更新数据时是全量重新同步,意味着 每天进行同步都会遭遇很大的性能影响,并且数据存在一天延迟于是尝试其他方法。 优点 如果使用数据库联合查询方案,可以直接跨库查询 支持OSS直接上传离线数据文件进行分析 缺点 虽然可以实现跨库查询,但是占用的还是生产库的计算资源,当需要复杂查询的时候可能影响用户体验 T+1方案存在数据延迟,且每天都是全量同步,在同步时对生产库有性能影响 C:使用clickhouse订阅多个mysql,实现数据聚合 方案优势 clickhouse在推出了支持MySQL实时复制后,可以很方便的订阅binlog实现clickhouse的数据库更新同步,实现准实时的数据分析 clickhouse数据库和用户使用的mysql数据库是完全隔离的,不会互相影响 无论是直接购买还是自建,相对其他大数据方案,成本较低 其他公司应用较多,携程,有赞等公司有在线上环境使用,参考资料较多 针对用户行为漏斗,留存等场景,有专有函数支持计算 metabase支持clickhouse数据库,原来的使用习惯不用改变,还是可以通过bi.xxx.com看数据 测试过程 ClickHouse,通过购买建立ClickHouse集群,通过购买DTS数据同步,将RDS数据库中的数据导入到ClickHouse集群中,可以实现不同数据的不同表导入到同一个数据库中,实现快速查询。 ClickHouse ,并且可以实时更新数据。 现ClickHouse配置为4核8G配置 1000G 属于按量付费,约1.5RMB/小时,30RMB/天,如需长期使用建议转为按月付费约600RMB/月, DTS数据同步 现购买两个每个约0.5RMB/小时,两个共计20RMB/天,如需长期使用建议按月付费每个400RMB/月 初始成本1400/月,后续可以视业务规模扩容升配置 线上业务性能对比 实例配置对比 mysql5.7 16核64G内存 clickhouse 20.3 4核8G 查询速度对比: Untitled 调研过程 1:尝试云原生数据湖的联合查询多个MySql实例,通过https://help.aliyun.com/document_detail/107698.html?spm=a2c4g.11186623.6.837.4d7974f4GsP1S0 可以实现不同数据库的多表联合查询,但是咨询工单反馈,查询过程仍再原数据库中执行,在小数量的数据库查询中影响不大,但是在查询量较大较频繁时,无法 保证原数据库的性能。于是尝试其他方法。 2:尝试创建云原生数据湖分析中的T+1多库合并建仓,将数据库导入到DLA数据湖中,通过自己新创建的数据量很小的数据库导入,可以实现,并且不会占用大量性能。 但针对test库的多库合并建仓时,因为性能原因造成较大的影响。并且询问阿里云工单之后反馈,多库合并建仓再第一次入湖之后,再每一次的更新数据时是全量重新同步,意味着 每天进行同步都会遭遇很大的性能影响,并且数据存在一天延迟于是尝试其他方法。 3:ClickHouse,通过购买建立ClickHouse集群,通过购买DTS数据同步,将RDS数据库中的数据导入到ClickHouse集群中,可以实现不同数据的不同表导入到同一个数据库中,实现 ...

March 16, 2021 · 1 min · 94 words · Jimmy

真不是我甩锅,细数我遇到过的阿里云的故障

网站时不时卡顿几分钟,自动恢复,搜索服务响应缓慢 影响范围:web,app都出现502 原因: 阿里云低版本ES存在稳定性问题 解决方案:升级ES内核,阿里云并不会自动升级 香港的serverless 容器 访问国内的网络有connect time out的问题,每天的晚上8点开始会突然多起来 影响范围:香港报价服务和国内交易服务的通讯 原因:运营商反馈:是运营商海缆故障,请您知悉。 解决方案:等待恢复 服务内调用服务出现超时 影响范围:一些服务异常,包括库存,短信等 原因:阿里云k8s集群自己改变了策略,导致我们之前的正常的流量策略不可用 解决方案:设置成新的策略 证书出现问题,无法访问 影响范围:人工发货功能不可用,用户无法使用我们的代理地址 原因:阿里云升级了k8s的配置,没有通知我们 解决方案:按新的方式配置证书 阿里云的RDS数据库读写分离地址突然失效 影响范围:半夜的时候造成网站完全无法访问 原因:阿里云故障 解决方案:我们当时立马申请了新的地址,才恢复访问 香港的serverless集群突然全部实例异常,流量无法进入 影响范围:部署在香港的服务全部无法问题 原因:阿里云故障 解决方案:等待阿里云修复之后才恢复正常,已经要求阿里云赔偿 香港的serverless集群绑定的弹性公网IP自动消失 影响范围:报价服务异常 原因:阿里云故障 解决方案:等阿里云修复 RocketMQ消费者全部离线故障 影响范围: 公司全部报价无法处理 原因: 阿里云旧的SDK存在断线重连重试BUG 解决方案: 升级新的SDK

February 10, 2021 · 1 min · 40 words · Jimmy

nginx https 配置

server { listen 80; listen 443; ssl on; ssl_certificate conf.d/jimersylee.com.crt; ssl_certificate_key conf.d/jimersylee.com.key.pem; ssl_session_timeout 5m; ssl_protocols SSLv2 SSLv3 TLSv1; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; ssl_prefer_server_ciphers on; root /etc/nginx/conf.d; # Add index.php to the list if you are using PHP index index.html index.htm index.nginx-debian.html; server_name *.jimersylee.com; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; } } 另外推荐,一个工具 https://www.digitalocean.com/community/tools/nginx 可视化配置nginx,方便新手了解nginx

February 3, 2021 · 1 min · 72 words · Jimmy