Seek-Wealth-Not-Money-or-Status(去寻找财富,而非金钱和地位)

Seek Wealth, Not Money or Status

去寻找财富,而非金钱和地位
Wealth is assets that earn while you sleep
财富是资产,资产在你你睡觉时也能一直为你赚钱

财富是资产,资产在你你睡觉时也能一直为你赚钱

Nivi: You probably know Naval from his Twitter account.
Nivi: 你可能通过Naval的Twitter账号认识他。

We’re going to talk about his tweetstorm, “How To Get Rich (without getting lucky).” We’ll go through most of the tweets in detail, give Naval a chance to expand on them and generally riff on the topic. He’ll probably throw in ideas he hasn’t published before.
我们将会讨论他的推文“How To Get Rich (without getting lucky)”如何致富(不靠运气)。我们将通过详细讨论大部分推文,让Naval有机会进一步阐述概括这个主题。他也有可能抛出新的想法。

Naval’s the co-founder of AngelList and Epinions. He’s also a prolific tech investor in companies like Twitter, Uber and many more.
Naval是AngelList和Epinions的联合创始人。他是一名丰富的科技投资人,投资过Twitter、Uber等公司。

I’m the co-founder of AngelList with Naval. And I co-authored the Venture Hacks blog with him back in the day.
和Naval共同创立了AngelList。也曾经一起撰写过Venture Hacks博客。

Naval: The “How to Get Rich” tweetstorm definitely hit a nerve and went viral. A lot of people say it was helpful and reached across aisles.
Naval:“How to Get Rich”(如何致富)这篇推文显然戳中了很多人的痛点,并迅速走红。很多人说它非常有帮助,而且打破了圈层,引发了不同群体的共鸣。

People outside of the tech industry—people in all walks of life—want to know how to solve their money problems. Everyone vaguely knows they want to be wealthy, but they don’t have a good set of principles to do it by.
不只是科技圈的人,几乎各行各业的人都想知道怎么解决财务上的困扰。大家都知道自己想变有钱,但很多人其实并没有一套靠谱的方法或原则去实现它。

Wealth is assets that earn while you sleep

财富是资产,资产在你睡觉时也能一直为你赚钱

Nivi: What’s the difference between wealth, money and status?
Nivi: 财富、金钱、地位有什么不同?

Naval: Wealth is the thing you want. Wealth is assets that earn while you sleep; it’s the factory of robots cranking out things. Wealth is the computer program running at night that’s serving other customers. Wealth is money in the bank that is reinvested into other assets and businesses.
Naval: 财富是你想要的。财富是资产,资产在你睡觉时也能一直为你赚钱;它可以是工厂里的机器人为你生产东西。也可以是晚上也能一直服务客户的电脑程序。财富是银行里的钱,可以再投资到其他资产或生意当中。

A house can be a form of wealth, because you can rent it out; although that’s a less productive use of land than running a commercial enterprise.
房子也可以是一种财富,因为你可以将它出租出去。尽管这与经营企业比,是一种生产力较低的一种利用土地的方式。

My definition of wealth is oriented toward businesses and assets that can earn while you sleep.
我对财富的定义是在你睡觉时能为你赚钱的企业和资产。

Wealth buys your freedom

财富买来的是自由

You want wealth because it buys you freedom—so you don’t have to wear a tie like a collar around your neck; so you don’t have to wake up at 7:00 a.m. to rush to work and sit in commute traffic; so you don’t have to waste your life grinding productive hours away into a soulless job that doesn’t fulfill you.
你想要财富,是因为财富能为你买来自由——让你不必每天戴着像狗链一样的领带,不必早上七点起床赶去上班、堵在通勤的车流中,不必把自己最有生产力的人生时光,浪费在一份毫无灵魂、不带来任何满足感的工作上。

The purpose of wealth is freedom; it’s nothing more than that. It’s not to buy fur coats, or to drive Ferraris, or to sail yachts, or to jet around the world in a Gulf Stream. That stuff gets really boring and stupid, really fast. It’s about being your own sovereign individual.
财富的真正目的就是自由,仅此而已。它不是为了买貂皮大衣、法拉利、游艇,也不是为了坐湾流私人飞机环球旅行。那些东西很快就会变得无聊甚至愚蠢。真正重要的是:成为一个完全自主、独立的人。

You’re not going to get that unless you really want it. The entire world wants it, and the entire world is working hard at it.
但你必须真正渴望这种自由,你才可能得到它。全世界的人都想要这种自由,也都在努力争取。

It is competitive to some extent. It’s a positive sum game—but there are competitive elements to it, because there’s a finite amount of resources right now in society. To get the resources to do what you want, you have to stand out.
在某种程度上,这是一场竞争。虽然它是一个正和游戏,但仍然有竞争的部分,因为当前社会的资源是有限的。如果你想获得资源、去实现你想做的事,你就必须脱颖而出。

Money is how we transfer wealth

金钱是如何交换财富的具现

Money is how we transfer wealth. Money is social credits; it’s the ability to have credits and debits of other people’s time.
金钱是社会信用。它代表你拥有他人时间借贷的能力。

If I do my job right and create value for society, society says, “Oh, thank you. We owe you something in the future for the work that you did. Here’s a little IOU. Let’s call that money.”
如果我做好了我的工作并对社会产生了价值,社会会说,“谢谢你!我们欠你一点什么,以后会补偿你过去做的这些事。给你一张欠条,我们把它叫做‘钱’。”

That money gets debased because people steal the IOUs; the government prints extra IOUs; and people renege on their IOUs. But money tries to be a reliable IOU from society that you are owed something for something you did in the past.
这种钱会被稀释,因为有人偷走了这些欠条;政府印了更多的欠条;也有人赖账不还。但钱本质上是在努力成为一个可靠的社会欠条,是社会对你过去行为的回报。

We transfer these IOUs around; money is how we transfer wealth.
我们在人与人之间交换这些欠条;金钱就是我们如何交换财富的具现。

Status is your rank in the social hierarchy

地位是你在社会体系中的排名

There are fundamentally two huge games in life that people play. One is the money game. Money is not going to solve all of your problems; but it’s going to solve all of your money problems. I think people know that. They realize that, so they want to make money.
从根本上说,人们在生活中玩着两种巨大的游戏。一个是金钱游戏。金钱并不能解决你所有的问题;但它却能解决你所有需要钱的问题。我认为人们知道这一点。他们意识到了这一点,所以他们想赚钱。

At the same time, deep down many people believe they can’t make it; so they don’t want any wealth creation to happen. They virtue signal by attacking the whole enterprise, saying, “Well, making money is evil. You shouldn’t do it.”
与此同时,许多人内心深处认为自己赚不到钱,所以他们不希望财富创造发生。他们通过攻击整个行业来发出道德信号,说:”赚钱是邪恶的。你不应该这么做”。

But they’re actually playing the other game, which is the status game. They’re trying to be high status in the eyes of others by saying, “Well, I don’t need money. We don’t want money.”
但实际上,他们在玩另一种游戏,即地位游戏。他们试图通过说 “我不需要钱” ,“我也不想要赚钱 ”来提高自己在别人心目中的地位。

Status is your ranking in the social hierarchy.
地位是你在社会体系中的排名。

Wealth is not a zero-sum game. Everybody in the world can have a house. Because you have a house doesn’t take away from my ability to have a house. If anything, the more houses that are built, the easier it becomes to build houses, the more we know about building houses, and the more people can have houses.
财富不是零和游戏。世界上每个人都可以有房子。你有房子并不会剥夺我有房子的能力。相反,房子建得越多,建房就越容易,我们对建房的了解就越多,就有越多的人可以拥有房子。

Wealth is a very positive-sum game. We create things together. We’re starting this endeavor to create a piece of art that explains what we’re doing. At the end of it, something brand new will be created. It’s a positive-sum game.
财富是一个正和的游戏。我们共同创造财富。我们正在努力创造一件艺术品,来解释我们正在做的事情。最后,我们会创造出全新的东西。这是一个正和游戏。

Status is a very old game

地位是一个非常古老的游戏

Status, on the other hand, is a zero-sum game. It’s a very old game. We’ve been playing it since monkey tribes. It’s hierarchical. Who’s number one? Who’s number two? Who’s number three? And for number three to move to number two, number two has to move out of that slot. So, status is a zero-sum game.
另一方面,地位是一种零和游戏。这是一种非常古老的游戏。从猴群时代开始,我们就一直在玩。它是等级制的:谁是第一?谁是第二?谁是第三?而要从第三升到第二,第二就必须让出那个位置。所以,地位是一种零和游戏。

Politics is an example of a status game. Even sports is an example of a status game. To be the winner, there must be a loser. Fundamentally, I don’t like status games. They play an important role in our society, so we can figure out who’s in charge. But you play them because they’re a necessary evil.
政治就是一种地位游戏的例子。甚至体育比赛也是一种地位游戏。要有赢家,就必须有输家。我本质上不喜欢这种地位游戏。它们在社会中确实扮演着重要角色——帮助我们弄清谁是领导者。但你之所以参与,是因为它们是一种“必要的恶”。

On an evolutionary basis—if you go back thousands of years—status is a much better predictor of survival than wealth. You couldn’t have wealth before the farming age because you couldn’t store things. Hunter-gatherers carried everything on their backs.
从进化的角度来看——如果你回顾几千年前——地位比财富更能预测一个人的生存能力。在农业出现之前,人们是无法拥有财富的,因为他们无法储存东西。狩猎采集者把一切都背在身上生活。

Hunter-gatherers lived in entirely status-based societies. Farmers started going to wealth-based societies. The modern industrial economies are much more heavily wealth-based societies.
狩猎采集者生活在完全基于地位的社会中。而农耕者开始向基于财富的社会转变。现代工业经济体则更多是基于财富的社会。

People creating wealth will always be attacked by people playing status games

创造财富的人,总会受到玩地位游戏的人的攻击

There’s always a subtle competition going on between status and wealth. For example, when journalists attack rich people or the tech industry, they’re really bidding for status. They’re saying, “No, the people are more important. And I, the journalist, represent the people, and therefore I am more important.”
地位和财富之间,总存在着一种微妙的竞争。比如,当记者抨击富人或科技行业时,其实是在争夺地位。他们是在表达:“人民才是最重要的,而我,作为记者,是人民的代表,所以我更重要。”

The problem is, to win at a status game you have to put somebody else down. That’s why you should avoid status games in your life—because they make you into an angry combative person. You’re always fighting to put other people down and elevate yourself and the people you like.
问题在于:想要在地位游戏中获胜,就必须贬低别人。这也是为什么你应该避免参与地位游戏——因为它会让你变得愤怒、好斗。你会不停地打压他人、抬高自己或你所支持的人。

Status games are always going to exist; there’s no way around it. Realize that most of the time when you’re trying to create wealth, you’re getting attacked by someone else and they’re trying to look like a goody-two shoes. They’re trying to up their own status at your expense.
地位游戏永远都会存在,这是无法避免的。你需要明白,大多数时候,当你试图创造财富时,攻击你的人,其实是在假装正义。他们想让自己看起来像是“正人君子”,但本质上是在踩着你来提高自己的地位。

They’re playing a different game. And it’s a worse game. It’s a zero-sum game, instead of a positive-sum game.
他们玩的是另一种游戏——一种更糟糕的游戏。地位游戏是零和游戏,而财富游戏则是正和游戏。

如何排查解决Communications link failure

  • 项目启动运行几小时后数据库连接异常,重启正常使用
  • 偶发数据库连接异常,重启正常使用
  • 偶发事物数据丢失

怎么办,网上搜索,druid issue搜索,一会儿让改一下这个参数,一会儿改一下那个参数。各种改了重启,观察。然后报错依然复现存在。没想到我们也有资格成为调参工程师。

环境

  • MySQL:8.0.32
  • Druid: 1.1.24
  • Driver:mysql-connector 8.0.27

错误异常

通常错误有以下几种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Caused by: java.net.SocketException: Connection reset

Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure
The last packet successfully received from the server was 1,254,739 milliseconds ago. The last packet sent successfully to the server was 1,254,740 millisecond ago.s ago.

Caused by: java.sql.SQLException: connection disabled
at com.alibaba.druid.pool.DruidPooledConnection.checkStateInternal(DruidPooledConnection.java:1169)

org.springframework.jdbc.UncategorizedSQLException:
### Error querying database. Cause: java.sql.SQLException: connection disabled

Cause: java.sql.SQLException: connection disabled
; uncategorized SQLException for SQL []; SQL state [null]; error code [0]; connection disabled; nested exception is java.sql.SQLException: connection disabled

2025-02-11 04:46:31.300 ERROR 1 --- [http-nio-8080-exec-7] [c.a.druid.pool.DruidPooledStatement :368] : CommunicationsException, druid version 1.1.24, jdbcUrl : jdbc:mysql://host:3306/db?useUnicode=true&characterEncoding=UTF8&zroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&useSSL=false&connectTimeout=10000&socketTimeout=120000, testWhileIle true, idle millis 6403958, minIdle 5, poolingCount 2, timeBetweenEvictionRunsMillis 60000, lastValidIdleMillis 6403958, driver com.mysql.cj.jdbc.Driver, exeptionSorter com.alibaba.druid.pool.vendor.MySqlExceptionSorter
2025-02-11 04:46:31.302 ERROR 1 --- [http-nio-8080-exec-7] [com.alibaba.druid.util.JdbcUtils :96] : close connection error

java.sql.SQLNonTransientConnectionException: Communications link failure during rollback(). Transaction resolution unknown.

排查步骤

以上所有的错误都是指向连接异常。需要我们抽丝剥茧。杀人是个罪恶的行径,查出真相是我的责任。

  • 网络问题
    作为程序员首先想到的不是程序有问题出,而是环境有问题。是否有防火墙杀死应用到数据库的连接

  • 数据库超时时间设置问题
    查看数据库设置超时时间是否和druid配置不匹配
    查看数据库设置

    1
    show variables like '%timeout%';

MySQL timeout
其中wait_timeout和interactive_timeout是设置MySQL在连接不操作多少秒后断开连接。
如:客户端连接到MySQL超过8小时没有任何操作,则MySQL将主动断开连接。其中wait_timeout为非交互的连接,我们应用程序连接使用的就是这个。
如果wait_timeout过小和druid配置连接检查间隔时间不合理,则有可能出现d以为该连接没关闭,但是MySQL已经主动关闭连接,导致连接异常的问题。但是这种情况较少,因为我们校验连接通常都是多少秒校验一次。
另外MySQL参数中也有事务时间,应用程序有长事务,导致超过MySQL设置事务时间,会导致事务不成功

另外我们可以通过查询MySQL的连接来等待判断时间

1
select * from information_schema.processlist where db = 'mydb'

MySQL processlist

  • druid配置不合理
    外部原因排查完后,就只剩下排查自己问题了。不对,也可以是druid提示不明显。
    我们通常将连接池托管给三方库,我们只需要按其提供建议配置就好。druid就是其中一种。各版本配置有些许变化,
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    shardingsphere:
    props:
    sql-show: true
    query-with-cipher-column: false
    datasource:
    names: salve,master
    master:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://${db.host}:${db.port}/${db.database}?useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&useSSL=false&connectTimeout=10000&socketTimeout=60000
    username: ${db.username}
    password: ${db.password}
    maxActive: ${db.maxActive:100}
    minIdle: ${db.minIdle:5}
    initialSize: ${db.minIdle:5}
    minEvictableIdleTimeMillis: ${db.minEvictableIdleTimeMillis:180000}
    maxEvictableIdleTimeMillis: ${db.maxEvictableIdleTimeMillis:300000}
    maxWait: ${db.maxWait:10000}
    maxWaitThreadCount: ${db.maxWaitThreadCount:1000}
    testOnBorrow: false
    testOnReturn: false
    testWhileIdle: true
    keepAlive: true
    timeBetweenEvictionRunsMillis: 20000
    validationQueryTimeout: 10
    validationQuery: SELECT 1
    ## TRANSACTION_READ_COMMITTED
    default-transaction-isolation: 2

加入配置druid.mysql.usePingMethod=false
在检查有效连接的时候才不会使用ping而使用validationQuery sql来检查
另外可以查看DruidDataSource配置属性列表

  • 事务未正确关闭
    在排除了非程序问题,非配置问题后,还没有找到原因。就要想是不是druid的源码问题,还是MySQL源码问题。或者可能是自己程序哪里有问题。在看了一阵druid的源码和issue后发现有跟程序未正确结束事务(commit or rollback),会导致这个事务持有数据库连接无法正常归还到连接池,从而导致连接池中可用连接变少。
    我的问题就是这个,修改后,解决了这个连接异常问题
    issue

MySQL MGR是什么?

术语

  • MGR:MySQL Group Replication,即MySQL组复制
  • RPO:Recovery Point Objective恢复点目标
  • RTO:Recovery Time Objective恢复时间目标

背景

公司核心应用A为公司核心应用,对数据库RPO、RTO都有较高要求。一次数据库主库磁盘expender背板坏了,影响磁盘IO通道,数据库层面出现大面积超时和错误。由于机器未完全坏掉,数据库可以连接,SQL可以执行,导致主从未能自动切换,手动切换时准备脚本,校验数据同步等问题耗时过长,导致公司业务出现重大损失。(原有方案为MHA高可用,半同步复制)
由此改为MGR集群技术方案,以期降低主从切换时效,降低损失。

MGR

MGR高可用方案中的RPO、RTO
RPO:架构模型保障了数据一致性,无需人为干预和检测
RTO:因其本身RPO的自动保障,无数据差异,准备耗时短,

MGR具备以下几个特点:

基于shared-nothing模式,所有节点都有一份完整数据,发生故障时可以直接切换。
MGR提供了数据一致性保障,默认是最终一致性,可根据业务特征需要自行调整一致性级别。
支持在线添加、删除节点,节点管理更方便。
支持故障自动检测及自动切换,发生故障时能自动切换到新的主节点,再配合MySQL Router中间件,应用层无需干预或调整。
支持单节点、多节点写入两种模式,可根据架构或业务需要选择哪种方案,不过强烈建议选用单主模式。

参考

https://dev.mysql.com/doc/refman/8.4/en/group-replication.html

https://greatsql.cn/blog-10-9.html

如何预估MySQL表空间占用大小

我们经常需要知道表空间大小,在修改表结构的时候需要知道表空间大小以预估影响时间;在设计表的时候需要预估表的数据量,预估磁盘空间等。

查看已存在的表空间大小

MySQL中information_schema有记录

1
2
3
SELECT concat(round(sum((data_length+index_length)/1024/1024),2),'MB' as data
FROM information_schema.tables
WHERE table_schema='mydb' and table_name='mytable';

新建表空间大小测算

从上面可以看到表空间大小由数据大小+索引大小两部分组成
下面通过一个例子来实际测算一下
DDL

1
2
3
4
5
6
7
8
9
10
mysql> desc City;
+-------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+----------+------+-----+---------+----------------+
| ID | int(11) | NO | PRI | NULL | auto_increment |
| Name | char(35) | NO | | | |
| CountryCode | char(3) | NO | MUL | | |
| District | char(20) | NO | | | |
| Population | int(11) | NO | | 0 | |
+-------------+----------+------+-----+---------+----------------+

  • 数据大小测算
    根据表结构中字段大小来测算
    66 bytes per row of data(4+35+3+20+4)

  • 索引大小测算
    4 bytes per row for the primary key
    7 bytes per row for country code index

    • 3 bytes for the country
    • 4 bytes for Clustered Key attached to the country code

这不考虑BTREE或表空间碎片的内务管理
对于一百万行,这将是77000000字节(73.43 MB)

参考
How to estimate/predict data size and index size of a table in MySQL

更详细的还需要看InnoDB : Tablespace Space Management

自定义ShardingSphere的JSON加解密器

基于数据安全的目的,需要对敏感数据进行加密存储。其中有整个字段是敏感内容的数据,也有部分灵活内容存储为JSON,JSON中的部分path为敏感内容。
针对这部分内容,需要对JSON指定path加解密,以避免对整个JSON加解密造成存储空间、应用缓存资源浪费。

ShardingSphere整体架构

加密规则

加密配置主要分为四部分:数据源配置,加密算法配置,加密表配置以及查询属性配置,其详情如下图所示:

JSON加解密器实现在加密配置->用户自定义处

配置

加密器类型配置
1
2
3
4
5
6
7
spring:
shardingsphere:
rules:
encrypt:
encryptors:
json_encryptor:
type: json
加密字段配置
1
2
3
4
5
6
7
8
9
10
11
spring:
shardingsphere:
rules:
encrypt:
tables:
t_user:
columns:
info:
plainColumn: info
cipherColumn: info_cipher
encryptorName: json_encryptor
JSON path加密配置
1
2
3
4
5
6
7
8
9
spring:
shardingsphere:
rules:
encrypt:
encryptors:
json_encryptor:
props:
column0_path: bankCard <!-- {唯一名字}_path=需要加密的json路径 -->
column0_path_encryptor: bankCard <!-- {唯一名字}_path_encryptor=加密类型 -->

自定义加密

接下来就是自定义ShardingSphere加密部分
可以查看ShardingSphere官网,EncryptAlgorithm
继承EncryptAlgorithm

  • 重写getType为json(加密器类型配置处使用)
  • 重写setProps(JSON path加密配置会从此处拿到)
  • 重写encrypt、decrypt,在遇到加密字段配置中的SQL时,触发改写SQL,可以根据props中配置的json path自定义加密。

实现SPI
在代码的resources路径下创建META-INF\services\org.apache.shardingsphere.spi.encrypt.ShardingEncryptor
文件内容为自定义加密类的全路径,如:com.company.shardingsphere.encrypt.JSONEncryptor

MySQL中的float、double的精度是如何丢失的?

前言

朋友在设计表的时候很疑惑小数的时候到底该用Float、Double还是Decimal,什么情况下使用?
我们总听说Float、Double会丢失精度,如果是金钱则使用Decimal。但是在业务场景里面,我们期望的是程序是可靠的,所有数据都是准确的。那是不是意味着所有的字段都要用Decimal,那Float、Double还有什么用?
所以我们需要理解到精度到底是怎么丢失的,什么情况下丢失,什么情况下不丢失?才能得出Float、Double在怎样情况下是可靠的,才能在需要使用的时候判断出该使用什么数据类型。

Float为什么会丢失?

Float、Double存储的是近似值。为什么是近似值,先看看各数据类型空间占用情况

类型名称 说明 存储需求
Float 单精度浮点数 4字节
Double 双精度浮点数 8字节
Decimal 压缩的“严格”定点数 Decimal(M,D),如果M>D,为M+2否则为D+2字节

存储Float、Double时采用将数据转换为二进制进行存储。
存储格式为

比如8.25用二进制表示可表示为1000.01,转成指数的形式1.00001*2^3,在计算机中

这其中小数的二进制计算方式与整数不同,需要使用小数部分2取整数,直到为0
例如0.32的二进制计算方式如下
0.32
2 = 0.64 0
0.642 = 1.28 1
0.28
2 = 0.56 0
0.562 = 1.12 1
0.12
2 = 0.24 0
0.242 = 0.48 0
0.48
2 = 0.96 0
0.962 = 1.92 1
0.92
2 = 1.84 1
0.842 = 1.68 1
0.68
2 = 1.36 1
0.36*2 = 0.72 0

对于这样整除不尽或者超过32位的情况,就一定会丢失精度,或者四舍五入后得到的近似值
针对float情况,至少我们可以得出结论:
1.如果一个float型数据转成二进制后的第32位之后都是0,那么数据是准的
2.如果一个float型数据转成二进制后的第32位之后不全为0,则数据就会存在误差

重新说明float(M, D)两个参数的意义

这两个参数表示一共能存M位,其中小数点后占D位。比如float(3,1)表示一共3位,其中小数点后1位数字。这里会有两个误区

数据的精度总是能精确到D位,也就是数据的不精确一定出现在小数点后
数据存储的时候只能存储到D位小数

  • 第一个误区,如果对于float4字节的存储空间连整数的存储不下的时候,连整数都有误差的,更何况小数,所以存储空间大小决定存储精度,和D值无关。来看这样一个例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    mysql> create table f2 (f1 float(15,2));
    Query OK, 0 rows affected (0.01 sec)
    mysql> insert into f2 values (123456789.39);
    Query OK, 1 row affected (0.00 sec)
    mysql> select * from f2;
    +--------------+
    | f1 |
    +--------------+
    | 123456792.00 |
    +--------------+
    1 row in set (0.00 sec)

    最后你会发现,连整数都不准了,小数被完全抹去了。

  • 第二个误区,对于存储而言,是和D无关的一个参数。因为浮点型数据最终都要被转成二进制进行存储。并且对于float,这个二进制只能有32位0和1的组合。看下面的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    mysql> select * from f;
    +-----------+
    | f1 |
    +-----------+
    | 131072.31 |
    +-----------+
    1 row in set (0.00 sec)
    mysql> alter table f modify f1 float(10,4);
    Query OK, 0 rows affected (0.02 sec)
    Records: 0 Duplicates: 0 Warnings: 0
    mysql> select * from f;
    +-------------+
    | f1 |
    +-------------+
    | 131072.3125 |
    +-------------+
    1 row in set (0.00 sec)

    可以看到,修改一下显示宽度D,这个时候可以看到MySQL真正存储的数字是131072.3125

怎么样才能存储一个准确的数据

如果采用float或者double类型的话,数据有时候完全准确的,有时候是不准确的,怎么才能存储一个准确的数字,完全看你需要存什么样的数据,假如存储一个8.25这样的数字,那永远都是准确的。但是如果存储0.9这样的数字,则永远存不准确。

所以如果一个实数在MySQL中存储准确的话,会出现以下三种情况

  • 数据真的准确,数据能在有限的存储空间里完全存储起来
  • 数据存储被截断,但是通过四舍五入依然能够将数据显示准确
  • 数据存储被截断,通过四舍五入不能将数字正确显示

关于decimal类型

通过前面的分析,了解了float和double类型的区别和误差来源。但是decimal类型是MySQL官方唯一指定能精确存储的类型,也是DBA强烈推荐和金钱相关的类型都要存储为decimal类型,如果猜想decimal类型的存储格式的话,那么一下两种可以保持数据的准确性

  • 继续扩大存储空间,比double更大一个级别,比如128位甚至更多
  • 通过字符串化或者其他的方式特殊存储起来

这两种方式都能实现decimal精确存储,但是由于MySQL指定decimal类型最大长度为65.在我们能测试的范围内,decimal并没有出现误差。

如何选择float,double,decimal

结论总是放在最后,根据上面的分析:可以得出以下结论

  1. 如果你要表示的浮点型数据转成二进制之后能被32位float存储,或者可以容忍截断,则使用float,这个范围大概为要精确保存6位数字左右的浮点型数据 比如10分制的店铺积分可以用float存储,小商品零售价格(1000块之内)

  2. 如果你要表示的浮点型数据转成二进制之后能被64位double存储,或者可以容忍截断,这个范围大致要精确到保存13位数字左右的浮点型数据 比如汽车价格,几千万的工程造价

  3. 相比double,已经满足我们大部分浮点型数据的存储精度要求,如果还要精益求精,则使用decimal定点型存储 比如一些科学数据,精度要求很高的金钱

写在最后

理论上的东西永远比不上实践,应用场景大于一切理论。选择float或者double或者decimal有时候也要看场景,比如我们可以用double存储一个小商铺的季度营业额(几千万),单独用double存储的时候没有问题,当多个季度,多个年份算总3年内的营业额是,就会出现问题,再也算不出一个准确的答案。所以,如果考虑情况没那么有把握的情况下,推荐使用decimal,最后,也可以通过其他手段避开这些问题,比如存储商品价格可以使用 乘以100的形式存储,展示价格的时候再除以100

B.3.4.8 Problems with Floating-Point Values
谈谈MySQL如何选择float, double, decimal

手机资费套餐

想找一个全网最低的资费套餐,网上也有0月租的,但是没有验证。
工信部查询出44个运营商官网拿到资费信息筛选出6元及以下的套餐如下

运营商 月租/资费 套餐名称 套餐详情 套餐URL
阿里通信 6 亲心6元套餐 60分钟国内语音
国内接听免费
国内语音0.15元/分钟
国内流量2元/日/随心用
当日有效
国内短信0.1元/条
赠送来电显示
https://aliqin.aliyuncs.com/#/prod
日日顺通信 5 顺意套餐 ①、国内语音拨打资费:0.15元/分钟;
②、国内流量:0.2元/M;
③、国内点到点短信:0.1元/条;
④、来电显示5元/月;
https://rrstel.com/businessHall/localpage/zifeizone.jsp
丰信移动 6 丰信6元A卡 1.月租6元/月,赠送来电显示,赠送60分钟国内语音
2.国内语音:0.15元/分钟
3.国内流量:1元包500M/日
4.国内短/彩信:0.1元/条。
http://www.phtion.com/account/index
蓝猫移动 3.9 蓝猫标准流量卡 https://www.lanmaomobile.com/?list_8/232.html
朗玛移动 6 小象阳光卡6元 语音:0分钟
流量:0GB
流量:0.1元/1M
短信:0.1元/条
语音:0.15元/分钟
https://www.langma.cn/langma-jx
天音移动 6 天音卡-联通版 打电话0.15元/分钟
上网流量0.2元/M
短信0.1元/条
彩信0.3元/条
来电显示月租5元
https://rrstel.com/businessHall/localpage/zifeizone.jsp
普泰移动 6 普泰惠享卡 https://rrstel.com/businessHall/localpage/zifeizone.jsp
苏宁互联 5 至简套餐 https://rrstel.com/businessHall/localpage/zifeizone.jsp
电信 5 无忧卡 适用范围:全部公众用户
有效期限:2025年11月30日
销售渠道:线下及线上渠道
可售范围:全国可售,但受各省销售安排所限
合约要求:不限制
https://www.189.cn/cq/zfzq/#tList_4

SPI机制

SPI(Service Provider Interface),是JDK内置的一种 服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用,比如java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现,MySQL和PostgreSQL都有不同的实现提供给用户,而Java的SPI机制可以为某个接口寻找服务实现。Java中SPI机制主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是 解耦
SPI整体机制图如下:
SPI

服务提供方提供了接口实现后,需要在classpath下的META-INF/services/目录下创建以服务接口命名的文件,文件内容为接口的实现类名。
其他程序使用服务时,会通过查找这个jar的META-INF/services/中文件,获取实现类名,进行加载实例化,该服务就可以使用了。JDK中查找服务实现的类为java.util.ServiceLoader。

应用-JDBC

JDBC接口定义

在java中定义了接口java.sql.Driver,并没有实现,具体实现由不通厂商实现。

MySQL实现

MySQL的jar包(mysql-connector-java-8.0.30.jar)中,META-INF/services目录下有文件名java.sql.Driver的内容为com.mysql.cj.jdbc.Driver
SPI META-INF/services

使用方法
1
Connection conn = DriverManager.getConnection(url,username,password);
SPI如何实现

在使用的时候并没有指定使用哪个Driver来连接,那如何使用上MySQL的驱动的呢?这就是我们SPI在起作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package java.sql;
public class DriverManager {

static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}

private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}

AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();

try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
}
return null;
}
});

println("DriverManager.initialize: jdbc.drivers = " + drivers);

if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
}

其中
ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
负责查找classpath下及jar包中META-INF/services目录下java.sql.Driver文件中的内容获取具体实现。

SPI机制的缺陷

通过上面的解析,可以发现,我们使用SPI机制的缺陷:

  • 不能按需加载,需要遍历所有的实现,并实例化,然后在循环中才能找到我们需要的实现。如果不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费。
  • 获取某个实现类的方式不够灵活,只能通过 Iterator 形式获取,不能根据某个参数来获取对应的实现类。
  • 多个并发多线程使用 ServiceLoader 类的实例是不安全的.

如何生成requirements.txt

写了一个简单的python项目,希望生成一个requirements.txt文件方便在Github或其他地方运行安装依赖。
作为新手我首先是一个一个从import里面去找到写到文件中的,结果发现居然会报错。
后面找到一个工具pipreqs可以自动识别出项目中用到的所有依赖生成requirements.txt文件

How

Installation

1
pip install pipreqs

Usage

1
2
$ pipreqs /home/project/location
Successfully saved requirements file in /home/project/location/requirements.txt

生成requirements.txt内容如

1
2
3
wheel==0.23.0
Yarg==0.1.9
docopt==0.6.2

更详细的参数用法参照pipreqs Usage

Java类文件在JVM运行的生命周期

java Class文件结构

Java .class 文件是 Java 编程语言的关键组件,遵循精确且定义的结构。 这种结构不仅对于 Java 虚拟机 (JVM) 正确加载和执行字节码至关重要,而且还提供了有关编译后的 Java 代码的大量信息。 下面,我们深入研究类文件结构的基本元素,详细说明每个组件及其在整体架构中的重要性。

class文件格式

类文件由单个 ClassFile 结构组成。 该结构由 JVM 规范定义并遵循特定格式,其中包括以下主要部分:

  • Magic Number魔数:固定值 (0xCAFEBABE)。 此唯一标识符验证该文件是否是 JVM 可读的有效类文件。
  • Version Information版本号:major_version、minor_version,java版本号
  • Constant Pool常量池
  • Access Flags访问标识
  • This Class, Super Class,and Interfaces类索引、父类索引与接口索引集合
  • Fields字段集合
  • Methods方法表集合
  • Attributes属性表集合

以上信息如何查看?

1
javap [options] classes...

JVM需要使用上述信息来正确加载、验证和执行

其是常量池,它是一个集中的字典,经常被类文件中的其他部分引用,突出了它在整个架构中的重要性。

Class文件在JVM中的生命周期

The Class file Lifecycle of a Java Application

1. Loading加载

类加载过程执行以下三个功能:
从clas文件创建二进制数据流
根据内部数据结构解析二进制数据
创建 java.lang.Class 的实例
完成此操作后,类实例就可以进行链接了。

2. Linking链接
2.1 Verification验证

此步骤可确保安全性和完整性。JVM验证class文件的正确性,文件格式验证、语法是否有效、是否符合Java语言规范。

2.2 Preparation准备

在准备过程中,JVM 会为类静态变量分配内存,并将其初始化为默认值。

2.3 Resolution解析

解析阶段包括将类文件中的符号引用解析为直接引用。这就是 JVM 常量池发挥关键作用的地方。主要针对类或接口、字段、类方法、方法类型等。

3. Initializes初始化

执行静态块: 这一阶段涉及执行静态初始化程序和静态块。JVM 会初始化静态字段,并按照它们在类文件中出现的顺序执行任何静态初始化块。
设置最终值: 为类的最终变量分配值,这些值在类的生命周期内不可更改。

4. Usage使用

实例化: JVM 根据应用程序的需要创建类的实例。
执行: 根据运行程序的要求调用和执行方法,访问字段。JVM 会将字节码解释或即时编译为机器代码以便执行。

5. Unloading卸载

垃圾回收: 当一个类不再需要,也没有对其实例的实时引用时,它就可以被卸载。JVM 的垃圾回收器会回收分配给类的内存。

The Anatomy of a Java Virtual Machine Class File
The Execution Lifecycle of a Java Application
javap
Chapter 4. The class File Format