TiDB 使用坑记录

https://blog.csdn.net/yajie_12/article/details/82773813

1、对硬盘要求很高,没上SSD硬盘的不建议使用

2、不支持分区,删除数据是个大坑。
解决方案:set @@session.tidb_batch_delete=1;

3、插入数据太大也会报错
解决方案:set @@session.tidb_batch_insert=1;

4、删除表数据时不支持别名
delete from 表名 表别名 where 表别名.col = ‘1’ 会报错

5、内存使用有问题,GO语言导致不知道回收机制什么时候运作。内存使用过多会导致TIDB当机(这点完全不像MYSQL)
测试情况是,32G内存,在10分钟后才回收一半。

6、数据写入的时候,tidb压力很大, tikv的CPU也占用很高

7、不支持GBK

8、不支持存储过程

9、列数支持太少,只支持100列,和oralce/mysql的1000列少太多(Oracle 最大列数为 1000;MySQL对于每个表具有4096个列的硬限制, 其中InnoDB每个表的限制为1017列, 最大行大小限制为65,535字节)

其他文章的一些建议

3TiKV+3PD+2TiDB

在有了 TiSpark 之后,我们便利用 TiSpark 将中间表缓存为 Spark 的内存表,只需要将最后的数据落地回 TiDB,再执行 Merge 操作即可,这样省掉了很多中间数据的落地,大大节省了很多脚本执行的时间

在查询速度解决之后,我们发现脚本中会有很多针对中间表 update 和 delete 的语句。目前 TiSpark 暂时不支持 update 和 delete 的操作(和 TiSpark 作者沟通,后续会考虑支持这两个操作),
我们便尝试了两种方案,一部分执行类似于 Hive,采用 insert into 一张新表的方式来解决;另外一部分,我们引入了 Spark 中的 Snappydata 作为一部分内存表存储,
在 Snappydata 中进行 update 和 delete,以达到想要的目的。因为都是 Spark 的项目,因此在融合两个项目的时候还是比较轻松的。

最后,关于实时的调度工具,目前我们是和离线调度一起进行调度,这也带来了一些问题,每次脚本都会初始化一些 Spark 参数等,这也相当耗时。在未来,我们打算采用 Spark Streaming 作为调度工具,
每次执行完成之后记录时间戳,Spark Streaming 只需监控时间戳变化即可,能够避免多次初始化的耗时,通过 Spark 监控,我们也能够清楚的看到任务的延迟和一些状态,这一部分将在未来进行测试。

TiDB 架构的演进和开发哲学

TiDB 架构的演进和开发哲学
https://segmentfault.com/a/1190000008288188


十问 TiDB :关于架构设计的一些思考
https://juejin.im/entry/5b29bde4518825749725c28f

其实并没有什么技术是完美和包治百病的,在存储领域更是如此,如果你的数据能够在一个 MySQL 装下并且服务器的压力不大,或者对复杂查询性能要求不高,其实分布式数据库并不是一个特别好的选择。 选用分布式的架构就意味着引入额外的维护成本,而且这个成本对于特别小的业务来说是不太划算的,即使你说需要高可用的能力,那 MySQL 的主从复制 + GTID 的方案可能也基本够用,这不够的话,还有最近引入的 Group Replication。而且 MySQL 的社区足够庞大,你能 Google 找到几乎一切常见问题的答案。

我们做 TiDB 的初衷并不是想要在小数据量下取代 MySQL,而是尝试去解决基于单机数据库解决不了的一些本质的问题。

有很多朋友问我选择分布式数据库的一个比较合适的时机是什么? 我觉得对于每个公司或者每个业务都不太一样,我并不希望一刀切的给个普适的标准(也可能这个标准并不存在),但是有一些事件开始出现的时候:比如是当你发现你的数据库已经到了你每天开始绞尽脑汁思考数据备份迁移扩容,开始隔三差五的想着优化存储空间和复杂的慢查询,或者你开始不自觉的调研数据库中间件方案时,或者人肉在代码里面做 sharding 的时候,这时给自己提个醒,看看 TiDB 是否能够帮助你,我相信大多数时候应该是可以的。

而且另一方面,选择 TiDB 和选择 MySQL 并不是一刀切的有你没他的过程,我们为了能让 MySQL 的用户尽可能减小迁移和改造成本,做了大量的工具能让整个数据迁移和灰度上线变得平滑,甚至从 TiDB 无缝的迁移回来,而且有些小数据量的业务你仍然可以继续使用 MySQL。所以一开始如果你的业务和数据量还小,大胆放心的用 MySQL 吧,MySQL still rocks,TiDB 在未来等你。


TiDB 安裝部署

https://php.wiki/tidb

TiDB 是新一代开源分布式 NewSQL 数据库,模型受 Google Spanner / F1 论文的启发,实现了自动的水平伸缩,强一致性的分布式事务,基于 Raft 算法的多副本复制等重要 NewSQL 特性。TiDB 结合了 RDBMS 和 NoSQL 的优点,部署简单,在线弹性扩容和异步表结构变更不影响业务, 真正的异地多活及自动故障恢复保障数据安全,同时兼容 MySQL 协议,使迁移使用成本降到极低。

安装部署

TiDB 官方提供了支持 Linux 版本的二进制安装包,官方推荐使用 Redhat 7+、CentOS 7+ 以上版本的操作系统,不推荐在 Redhat 6、CentOS 6 上部署 TiDB 集群。
操作系统:Linux ( Redhat 7+,CentOS 7+ )
执行步骤:

下载压缩包

wget http://download.pingcap.org/tidb-latest-linux-amd64.tar.gz
wget http://download.pingcap.org/tidb-latest-linux-amd64.sha256

检查文件完整性

,返回 ok 则正确
sha256sum -c tidb-latest-linux-amd64.sha256

解开压缩包

tar -xzf tidb-latest-linux-amd64.tar.gz
cd tidb-latest-linux-amd64

单节点方式快速部署

在获取 TiDB 二进制文件包后,我们可以在单机上面,运行和测试 TiDB 集群,请按如下步骤依次启动 PD,TiKV,TiDB。
注意:以下启动各个应用程序组件实例的时候,请选择后台启动,避免前台失效后程序自动退出。
步骤一. 启动 PD:
./bin/pd-server --data-dir=pd --log-file=pd.log
步骤二. 启动 TiKV:
./bin/tikv-server --pd="127.0.0.1:2379" --data-dir=tikv --log-file=tikv.log
步骤三. 启动 TiDB:
./bin/tidb-server --store=tikv  --path="127.0.0.1:2379"   --log-file=tidb.log
步骤四. 使用 MySQL 客户端连接 TiDB:
mysql -h 127.0.0.1 -P 4000 -u root -D test

参考链接

MySQL vs. TiDB

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

https://db-engines.com/en/system/MySQL%3BTiDB

https://opensource.com/article/18/11/key-differences-between-mysql-and-tidb

https://www.zhihu.com/question/58767602
TiDB 具备如下 NewSQL 核心特性:

SQL支持 (TiDB 是 MySQL 兼容的)---->基本上没有学习成本和开发改造成本,可以实现无缝后端存储迁移,这一点对开发哥来说太太太重要了。

水平线性弹性扩展            ---->无限的计算能力,无限的存储能力,虽然是理论上的,但是不觉得很牛逼的样子吗?

分布式事务                      -->适配OLTP场景

跨数据中心数据强一致性保证            ---->某些场景下很有用

故障自恢复的高可用            ---->永远挂不了的系统,是不是很酷?

TiDB 的设计目标是 100% 的 OLTP 场景和 80% 的 OLAP 场景。

没有学习成本和接入成本,不需要运维,随意扩展,支持事务,原生高可用,mysql遇到的问题在tidb里基本都解决了。



TiDB 在小米的应用实践
https://zhuanlan.zhihu.com/p/51472404

目前 TiDB 主要应用在:

小米手机桌面负一屏的快递业务
商业广告交易平台素材抽审平台

这两个业务场景每天读写量均达到上亿级,上线之后,整个服务稳定运行;接下来我们计划逐步上线更多的业务场景,小米阅读目前正在积极的针对订单系统做迁移测试。

TiDB 结合了传统的 RDBMS 和 NoSQL 的最佳特性,兼容 MySQL 协议,支持无限的水平扩展,具备强一致性和高可用性。

具有如下的特性:

高度兼容 MySQL,大多数情况下无需修改代码即可从 MySQL 轻松迁移至 TiDB,即使已经分库分表的 MySQL 集群亦可通过 TiDB 提供的迁移工具进行实时迁移。

水平弹性扩展,通过简单地增加新节点即可实现 TiDB 的水平扩展,按需扩展吞吐或存储,轻松应对高并发、海量数据场景。

分布式事务,TiDB 100% 支持标准的 ACID 事务。

真正金融级高可用,相比于传统主从(M-S)复制方案,基于 Raft 的多数派选举协议可以提供金融级的 100% 数据强一致性保证,且在不丢失大多数副本的前提下,可以实现故障的自动恢复(auto-failover),无需人工介入。

TiDB 的架构及原理官网有详细介绍(https://pingcap.com/)。


TiDB 基础架构图

跟绝大数互联网公司一样,小米关系型存储数据库首选 MySQL,单机 2.6T 磁盘。由于小米手机销量的快速上升和 MIUI 负一屏用户量的快速增加,导致负一屏快递业务数据的数据量增长非常快,每天的读写量级均分别达到上亿级别,数据快速增长导致单机出现瓶颈,比如性能明显下降、可用存储空间不断降低、大表 DDL 无法执行等,不得不面临数据库扩展的问题。比如,我们有一个业务场景(智能终端),需要定时从几千万级的智能终端高频的向数据库写入各种监控及采集数据,MySQL 基于 Binlog 的单线程复制模式,很容易造成从库延迟,并且堆积越来越严重。

对于 MySQL 来讲,最直接的方案就是采用分库分表的水平扩展方式,综合来看并不是最优的方案,比如对于业务来讲,对业务代码的侵入性较大;对于 DBA 来讲提升管理成本,后续需要不断的拆分扩容,即使有中间件也有一定的局限性。同样是上面的智能终端业务场景,从业务需求看,需要从多个业务维度进行查询,并且业务维度可能随时进行扩展,分表的方案基本不能满足业务的需求。

了解到 TiDB 特点之后,DBA 与业务开发沟通确认当前 MySQL 的使用方式,并与 TiDB 的兼容性做了详细对比,经过业务压测之后,根据压测的结果,决定尝试将数据存储从 MySQL 迁移到 TiDB。经过几个月的线上考验,TiDB 的表现达到预期。

兼容性对比

TiDB 支持包括跨行事务、JOIN、子查询在内的绝大多数 MySQL 的语法,可以直接使用 MySQL 客户端连接;对于已用 MySQL 的业务来讲,基本可以无缝切换到 TiDB。
二者简单对比如下几方面:

1. 功能支持

TiDB 尚不支持如下几项:

增加、删除主键
非 UTF8 字符集
视图(即将支持)、存储过程、触发器、部分内置函数
Event
全文索引、空间索引

2. 默认设置

字符集、排序规则、sql_mode、lower_case_table_names 几项默认值不同。

3. 事务

TiDB 使用乐观事务模型,提交后注意检查返回值。
TiDB 限制单个事务大小,保持事务尽可能的小。

4. TiDB 支持绝大多数的 Online DDL。

5. 另,一些 MySQL 语法在 TiDB 中可以解析通过,不会产生任何作用,例如: create table 语句中 engine、partition 选项都是在解析后忽略。

6. 详细信息可以访问官网:https://pingcap.com/docs-cn/sql/mysql-compatibility/ 。




關於 LevelDB

https://juejin.im/post/5c22e049e51d45206d12568e

它是 Google 开源的 NOSQL 存储引擎库,是现代分布式存储领域的一枚原子弹。在它的基础之上,Facebook 开发出了另一个 NOSQL 存储引擎库 RocksDB,沿用了 LevelDB 的先进技术架构的同时还解决了 LevelDB 的一些短板。你可以将 RocksDB 比喻成氢弹,它比 LevelDB 的威力更大一些。现代开源市场上有很多数据库都在使用 RocksDB 作为底层存储引擎,比如大名鼎鼎的 TiDB。


LevelDB vs. MySQL

https://db-engines.com/en/system/LevelDB%3BMySQL

MySQL Replication 主從式架構

https://blog.toright.com/posts/5062/

資料庫可以說是應用程式的核心,但關聯式資料庫 (RDBMS) 基於 ACID 設計原則,傳統架構導致在運算能力的擴展上,變得不容易實現。擴充上除了典型的 Scale-up (擴充單一硬體能力) 之外,最常使用的就是 Replication 技術,Replication 泛指資料複製模式,會把資料完整的複製到另一個 MySQL 節點上,這樣的方式並不是分散儲存喔。

目前常使用且成熟的關聯式資料庫,一般官方都會提供 Replication 機制,通常官方內建的 Replication 機制已經很成熟,不像幾年前 Open Source RDBMS 都要自己編譯第三方 Replication 功能,當然也有第三方像是 Percona 這樣的套件。透過 Replication 將資料同步複製到其他主機,可以實現運算的擴充與高可用架構。大部分的資料庫應用情境都是 SELECT 居多,最常用到的就是讀寫分離的「主從架構」,如下圖所示:


主要的 MySql Master 節點負責「讀寫」,寫資料會透過 Binlog Relay 機制,以非同步模式複製 Binlog 到每一台 Slave 執行 (正確一點的說法應該是 Slave 透過 REPLICATION 權限連線到 Master 去抓 Binlog 回來執行),運作時就好像讓 Slave 與 Master 進行資料同步。一般來說,Slave 不提供寫入功能,只負責分散 SELECT 運算,這也是最典型的擴充架構。實作時有一個地方很麻煩,就是應用程式必須自行實作「讀寫分離」的機制 (有些 Framework ORM 會內建),自行再程式邏輯將 SELECT Query 分散到 Slave 執行,除非有使用商業的 Proxy Connection Pool 機制,才有提供自動讀寫分離的機制。如果是使用 MariaDB 的話也可以搭配 MaxScale 實現讀寫分離機制,但 MaxScale 我沒玩過,只是打打嘴砲。

此外,建構 Replication 還有一個很重要的好處,就是可以順便備份!一般的情況我們都是用 mysqldump 進行資料備份對吧!?但是如果你的資料已經大到一定的程度,想要 Dump DB 絕對是 GG,搞不好跑個日備份就超過一天了....... 這時候就要借助 Replication 的能力。


透過 HAProxy 實現 MySQL 主備自動切換

上述說明的主從架構只能分散運算,並無法實現 HA 能力,如果採用 Master/Master 的配置方式就可以實現了,但是應用程式要如何判斷其他一台機器出問題呢?我們可以藉由 HAProxy 這個套件來實現。將其中一台機器上線,另一台基器備援 (Replication),當第一台機器發生問題時,自動由 HAProxy 轉接連線至備援機,以實現 HA 模式。HAProxy 的設定方式在 GitHub 上:https://github.com/samejack/blog-content/blob/master/mysql-replication/haproxy.cfg


MySQL 的日常管理

status;
show create database 資料庫名稱 ;
show create table 資料表名稱 ;
show table status ( from 資料庫名稱 ) ;
show columns from 資料表名稱 ;

select version();
show variables like 'have%ssl%';
show variables like 'port';
show variables like 'datadir';
sudo find / -name my.cnf
sudo mysql --help | grep my.cnf


檢視當前的工作狀態
mysql> show processlist
mysql> show full processlist

如果需要刪掉某個工作
mysql> kill 該工作的Id ;

檢視當前配置的最大連線數、當前使用過的最大連線數。
mysql> show variables like "max_c%";
mysql> show global status like "Max_%";

最大連線數設定為:使用過最大連線數/配置最大連線數≈85% 為比較合適,但使用過的最大連線數都受到配置最大連線數的應用,所以在配置最大連線數過少的情況下,儘可能的先配置大一點的連線數,過段時間獲取使用過的最大連線數後,再設定最優的最大連線數。

mysql> show variables like "%timeout%";

http://jeremy.zawodny.com/blog/archives/011421.html
無論如何都必須修改的四個 MySQL 預設值
max_connect_errors = 1844674407370954751
(並 running an occasional FLUSH HOSTS command)
connect_timeout = 20
skip-name-resolve

設置 max_connect_errors 的目的是預防暴力破解密碼,
所以是否應該如上建議的修改調高這個預設值,其實仍得端視實際應用場景的整體規劃,
比如防火牆是否有開放外部連結的埠號等等,
同理,調整每個預設值之前,應仔細思考實際應用場景的特殊需求,整體規劃考量。

mysql>
set GLOBAL max_connect_errors=1844674407370954751;
set GLOBAL max_connections=5000;
set GLOBAL connect_timeout=20;
set wait_timeout=600;
set interactive_timeout=600;


wait_timeout 預設值為 28800(8小時)
太長了會吃掉 mysql 的連線數

wait_timeout 和 interactive_timeout
這兩個參數要同時設定才會生效


mysql> show status
Show Status官網詳細文件:
https://dev.mysql.com/doc/refman/5.7/en/server-status-variables.html#statvar_Aborted_clients



RPC (Remote Procedure Call) 遠程過程調用,遠端程序呼叫

https://www.zhihu.com/question/25536695

https://zh.wikipedia.org/wiki/%E9%81%A0%E7%A8%8B%E9%81%8E%E7%A8%8B%E8%AA%BF%E7%94%A8


RPC, 遠程過程調用直觀說法就是A通過網絡調用B的過程方法。

通信中的協議是你自己規定的,比如你可以規定說當A向B發送數字1, B就打印hello world, 並返回數字1給A, 如果發送數字2,B就打印hello, guy.並發送數字2給A。

這就是一個簡單的RPC示例,實際上natty就是乾這個的,只不過它提供一套框架給你,讓你可以定義自己的規則,實現B裡面的函數。

我們來想一想,當A要把數字1發送給B要怎麼辦呢,你需要用socket,B是server, A是client, B執行完以後再把1通過socket寫回即可。你可以用AUPE書中的例子很容易就構建出來。

問題到這裡,應該就結束了,事實上就是這麼簡單,那為什麼需要RPC框架呢?

當你給B發送1或者2就可以區分函數方法,但是你不可能寫100個函數打印100個字符,你需要在發送1的時候,再接著發送一個字符串,這樣B只要實現一個通用的print函數就夠了。

我們繼續思考,當你想發送一個數字比如65534給B的時候,我們知道socket是按照字節接受數據的,一個字節最大為255也就是0xff, 如果要發送65534你有兩種方法,第一把65534當作5個字符6,5,5,3,4然後讓socket接受5個字符。第二,你可以把65534變成二進制0xfffe, 這樣你可以先發送一個0xff再發送一個0xfe,也就是先發一個255再發一個254再拼裝。

另外在拼裝這255和254的時候,我們知道不同體系結構的CPU字節序是不一樣的,因此,你需要解決大端小端字節序的問題。否則對於一個32位機器上,65534作為一個int型可能會是0xfffe0000,或者0x0000feff,這取決於你如何發送和打包它。

對於相對複雜的RPC, 我們發送的一個數據包往往既包含字符串又包含數字,這樣我們就需要把他們切割開來然後分別解碼編碼。

這個部分就叫做序列化反序列化,在高級一點的RPC框架中,甚至可以做到把一個類從A扔給B.用到的還是這個方法,只是把類這個類型也放進去了。

對於腳本語言比如Lua,你甚至可以把編譯過後的字節碼發給遠程,然後在B用Lua虛擬機執行。

所以對於一個完整的RPC框架底層往往是socket搭配序列化反序列化的工作。

問題到這裡,一個RPC分析應該也結束了, 實際上要想把一個RPC做穩定了,還有幾個問題。

首先,要想讓一個RPC能夠更高效,往往做到讓A可以連續向B發送請求,這就要求在協議解析的時候區分兩個包,在TCP分包的時候這裡有個問題叫做半包和粘包。

另外B也無需在返回完函數1以後再返回函數2,這就要求在返回的時候,A能夠區分開來,這就要求在協議裡面加入session.

高效的寫法不但在編解碼和協議設計理念,還需要在編寫socket server時候,盡可能降低阻塞調用,用異步來做,這有一大推開源方案,比如libev.netty就是一個這樣異步的RPC通信框架。

當然,在實際項目中,我們只是需要解決實際問題,完全沒必要從頭構建,你可以選一個通信協議比如protobuf/JSON/msgpack/thrift等等再找他相關的RPC庫使用即可。





RPC:远程调用。通过RPC框架,使得我们可以像调用本地方法一样地调用远程机器上的方法:

1、本地调用某个函数方法

2、本地机器的RPC框架把这个调用信息封装起来(调用的函数、入参等),序列化(json、xml等)后,通过网络传输发送给远程服务器

3、远程服务器收到调用请求后,远程机器的RPC框架反序列化获得调用信息,并根据调用信息定位到实际要执行的方法,执行完这个方法后,序列化执行结果,通过网络传输把执行结果发送回本地机器

4、本地机器的RPC框架反序列化出执行结果,函数return这个结果

服務調用端 (本地機器)

服務提供端 (遠程機器)

單一 RPC 無法實現 push,即推送服務。理由是,RPC 是client 調用 server獲取數據,是一個完整的過程,實現不了server調用client。解決方案:讓client 既可以調用server上的RPC服務,反之client本身也成為一個RPC服務讓Server來調用。


比如說 Java Netty 是在TCP(Socket)層對nio進行封裝的框架,在RPC框架中可用於解決網絡傳輸問題。

現在流行的微服務框架(dubbo、spring cloud等),實際上就是各種各樣的RPC框架

1. Netty只是網絡通信框架,目的是讓你用最少的代碼構建出足夠支撐網絡通信的功能。

2.完成RPC 需要兩個協議: 對象序列化協議 和 調用控制協議



==>
早期單機時代,一台電腦上運行多個進程,大家各干各的,老死不相往來。假如A進程需要一個畫圖的功能,B進程也需要一個畫圖的功能,程序員就必須為兩個進程都寫一個畫圖的功能。這不是整人麼?於是就出現了IPC(Inter-process communication,單機中運行的進程之間的相互通信)。 OK,現在A既然有了畫圖的功能,B就調用A進程上的畫圖功能好了,程序員終於可以偷下懶了。

到了網絡時代,大家的電腦都連起來了。以前程序只能調用自己電腦上的進程,能不能調用其他機器上的進程呢?於是就程序員就把IPC擴展到網絡上,這就是RPC(遠程過程調用)了。現在不僅單機上的進程可以相互通信,多機器中的進程也可以相互通信了。

要知道實現RPC很麻煩呀,什麼多線程、什麼Socket、什麼I/O,都是讓咱們普通程序員很頭疼的事情。於是就有牛人開發出RPC框架(比如,CORBA、RMI、Web Services、RESTful Web Services等等)。

OK,現在可以定義RPC框架的概念了。簡單點講,RPC框架就是可以讓程序員來調用遠程進程上的代碼一套工具。有了RPC框架,咱程序員就輕鬆很多了,終於可以逃離多線程、Socket、I/O的苦海了。

Netty、Mina是遊戲行業做服務器開發的Java程序員用的比較多的PRC框架。據說互聯網公司用的也比較多。這兩行業都有高並發量的、長連接、分佈式、異步通訊、大數據量等特點。 Netty這種RPC框架封裝和優化了Java NIO和異步網絡編程的一些繁瑣的細節,一方面可以讓開發者專注於業務邏輯的實現,一方面只需要調用Netty封裝的API就可以很快編寫出高性能的服務器。



==>
先说说原理。

本地过程调用
RPC就是要像调用本地的函数一样去调远程函数。在研究RPC前,我们先看看本地调用是怎么调的。假设我们要调用函数Multiply来计算lvalue * rvalue的结果:
1 int Multiply(int l, int r) {
2    int y = l * r;
3    return y;
4 }
5 
6 int lvalue = 10;
7 int rvalue = 20;
8 int l_times_r = Multiply(lvalue, rvalue);
那么在第8行时,我们实际上执行了以下操作:
  1. 将 lvalue 和 rvalue 的值压栈
  2. 进入Multiply函数,取出栈中的值10 和 20,将其赋予 l 和 r
  3. 执行第2行代码,计算 l * r ,并将结果存在 y
  4. 将 y 的值压栈,然后从Multiply返回
  5. 第8行,从栈中取出返回值 200 ,并赋值给 l_times_r
以上5步就是执行本地调用的过程。(20190116注:以上步骤只是为了说明原理。事实上编译器经常会做优化,对于参数和返回值少的情况会直接将其存放在寄存器,而不需要压栈弹栈的过程,甚至都不需要调用call,而直接做inline操作。仅就原理来说,这5步是没有问题的。)

远程过程调用带来的新问题
在远程调用时,我们需要执行的函数体是在远程的机器上的,也就是说,Multiply是在另一个进程中执行的。这就带来了几个新问题:
  1. Call ID映射。我们怎么告诉远程机器我们要调用Multiply,而不是Add或者FooBar呢?在本地调用中,函数体是直接通过函数指针来指定的,我们调用Multiply,编译器就自动帮我们调用它相应的函数指针。但是在远程调用中,函数指针是不行的,因为两个进程的地址空间是完全不一样的。所以,在RPC中,所有的函数都必须有自己的一个ID。这个ID在所有进程中都是唯一确定的。客户端在做远程过程调用时,必须附上这个ID。然后我们还需要在客户端和服务端分别维护一个 {函数 <--> Call ID} 的对应表。两者的表不一定需要完全相同,但相同的函数对应的Call ID必须相同。当客户端需要进行远程调用时,它就查一下这个表,找出相应的Call ID,然后把它传给服务端,服务端也通过查表,来确定客户端需要调用的函数,然后执行相应函数的代码。
  2. 序列化和反序列化。客户端怎么把参数值传给远程的函数呢?在本地调用中,我们只需要把参数压到栈里,然后让函数自己去栈里读就行。但是在远程过程调用时,客户端跟服务端是不同的进程,不能通过内存来传递参数。甚至有时候客户端和服务端使用的都不是同一种语言(比如服务端用C++,客户端用Java或者Python)。这时候就需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式。这个过程叫序列化和反序列化。同理,从服务端返回的值也需要序列化反序列化的过程。
  3. 网络传输。远程调用往往用在网络上,客户端和服务端是通过网络连接的。所有的数据都需要通过网络传输,因此就需要有一个网络传输层。网络传输层需要把Call ID和序列化后的参数字节流传给服务端,然后再把序列化后的调用结果传回客户端。只要能完成这两者的,都可以作为传输层使用。因此,它所使用的协议其实是不限的,能完成传输就行。尽管大部分RPC框架都使用TCP协议,但其实UDP也可以,而gRPC干脆就用了HTTP2。Java的Netty也属于这层的东西。
所以,要实现一个RPC框架,其实只需要把以上三点实现了就基本完成了。
Call ID映射可以直接使用函数字符串,也可以使用整数ID。映射表一般就是一个哈希表。
序列化反序列化可以自己写,也可以使用Protobuf或者FlatBuffers之类的。
网络传输库可以自己写socket,或者用asio,ZeroMQ,Netty之类。

最后,有兴趣的可以看一个小而精的RPC库 tinyrpc(hjk41/tinyrpc),对于理解RPC如何工作很有好处。



==>
我们在做一个访问量不大的项目的时候,一台服务器部署上一个应用+数据库也就够了.

那么访问量稍微大一点之后呢,为了解决用户反馈的卡,反应慢的情况,我们就上集群.架设nginx,部署多个服务,由nginx负责把请求转发到其他服务上,这样就解决了用户说的卡慢问题.

过了一段时间之后呢,我们发现数据库已经扛不住了,应用服务完好,数据库有时候宕机. 那这个时候呢,我们就上数据库读写分离,再架设几台数据库服务器,做主从,做分库分表. 然后数据库也不宕机了,服务又恢复了流畅.

又过了一段时间,公司事业增增日上,服务访问量越来越高,且大部分都是查询, 吸取之前宕机且为了办证数据库的健壮性,我们这个时候又加上了缓存, 把用户高频次访问的数据放到缓存里.

后来,项目功能越来越多,整个项目也愈发庞大,修改一个类就需要全盘上传,切换nginx重启,这样的发布流程越来越长,越来越繁杂.然后我们开始把模块拆分,用户信息分个项目,订单系统分一个项目.这样就达到了,用户模块代码修改的时候,只需要更新用户信息服务就好了.但是还是需要切换顶层的nginx.把要重启的服务的流量切到可用服务上. 这个时候我们就想到了RPC

那么RPC解决了什么呢? 所有的服务在启动的时候注册到一个注册机里面,然后顶层处理在接收到nginx的请求时,去注册机找一个可用的服务,并调用接口. 这样子呢,在不加新功能的时候,顶层处理服务我们就不需要动了? 那修改了用户信息项目的时候,我们只需要一个个更新用户信息项目的服务群就好了?

这样的话,无论是扩展还是服务的健壮性都妥妥的了?


==> 你妈妈电脑系统出问题了,你使用qq远程助手,帮她修复系统,这中间,你做的每一步都可以理解为一个rpc调用,本质上就是想操作你本机的代码一样调用远程机器上的代码


html 頁面內錨點定位及跳轉方法

一種方法是錨點用<a>標籤,在href屬性中寫入DIV的id。

<a href="#div1">to div1</a>
<a href="#div2">to div2</a>
<a href="#div3">to div3</a>
<div id="div1">div1</div>
<div id="div2">div2</div>
<div id="div3">div3</div>




另一種方法是用 JavaScript 的 srollIntoView方法,

document.getElementById("divId").scrollIntoView();

這種方法的好處,是URL不會變,同時能夠響應相應的scroll事件

程式語言編年史

程式語言編年史原文 下面這張圖片描繪了整個程式語言的歷史。包括各種程式語言的發明人、程式語言的特點和適用領域、被什麼網站或公司使用等 (檢視 完整高清圖 )。 之所以會有那麼多不同的程式語言是因為設計程式語言的初衷不同、對語言學習曲線的追求不同、不同程式之間的執行成本差異...