对于任何一门语言,网络编程都是重要的一部分。对于Go来说其天生的高并发网络编程更是充满魅力。所以今天开学学习Go网络编程部分,教材是 Jan Newmarch 的 Network programming with Go 的pdf文档。 这份文档在 这里 可以找到,由 Astaxie 翻译的中文版也在这个网站的这个目录, 当然也可以在astaxie的github上找到。

我使用英文的pdf,因为没有找到中午的pdf,而使用pdf比网页要方便些,方便做笔记和添加备注,并且这类文档的英文应该也不会太难。

根据pdf的结构,一节一节往后走。各章节组成如下:

  1. 架构
  2. Go语言概括
  3. Socket编程
  4. 数据序列化
  5. 应用层协议
  6. 管理字符集与编码
  7. 安全
  8. HTTP
  9. 模板
  10. 一个完整的Web服务
  11. HTML
  12. XML
  13. 远程过程调用RPC
  14. 网络 Channels
  15. Web Sockets

其中前两张只简单的过一下,重要的是这几节: Socket, 序列化,应用层协议,安全,HTTP。 RPC与Web Sockets。其他章节看起来简单过一下就可以了。

阅读全文 »

视图

视图提供了在真是数据表上建立抽象数据表的功能,可以封装数据表结构的细节,是一种很好的sql设计。实际中也会经常使用视图,使用基于视图的查询会方便很多。
创建视图的语法和mysql很像,我们知道mysql实现视图有两种算法,临时表方法和~~忘了另一种是什么了,它们的实现区别导致它们的性能有差异。在pg中创建视图似乎不需要考虑这么多,直接创建就可以了。

create view view_name as select subcluase

select 子句中还可以基于其它视图查询,基于视图创建视图也是很常见的。不过可能太多层的视图创建会对查询性能有影响。要设计好的表结构,尽量避免多层的视图依赖。

外键

MySQL 的MyISAM引擎是不支持外键的,只有Innodb引擎可以支持外键。pg由于并没有多引擎的设计,所以在建表时不需要考虑这些,因为它就是支持外键的。

实际中,很多人并不太喜欢使用外键,比如我自己在设计数据库的时候也不喜欢用外键。一个是外键理解起来有点麻烦;二是外键的级联删除总觉得不太放心,不是自己操作的嘛。。。在使用外键的地方使用一张关联表也是很方便的。在插入删除的时候也就是多一句操作。

这里给一个官方的使用外键的例子,理解起来还是很方便的。

CREATE TABLE cities (
city varchar(80) primary key,
location point
);

CREATE TABLE weather (
city varchar(80) references cities(city),
temp_lo int,
temp_hi int,
prcp real,
date date
);

也许使用外键是一个不错的习惯,以后的数据表应该学习使用外键。

事物

事物使得一系列数据库操作打包成一个动作,保证中间不被打断以保证数据完整性。当其中一个操作出错时,系统会自动回滚到最初的状态,即这是一个 all-ot-nothing 的操作。
一个常见的食物操作的场景是转账或者付款交易。要保证A账号转出的数额与B账号受到的数额相等,不能A转出了B没有收到,也不能A还没转B就收到了。转账操作在数据库中至少需要两条语句,要保证这两天语句要么都成功,要么就都失败。
另外还要保证事物处理过程中,对外界是不可见的。

The updates made so far by an open transaction are invisible to other transactions until the transaction completes, whereupon all the updates become visible simultaneously

pg中的事物非常的简单,只要是在 BEGIN;COMMIT; 之间的操作就被看错一个事物。当然放到现实业务系统中,不同的驱动有不同的实现。
实际上可以把所有的语句都理解为事物,这是一个有意思的理解

PostgreSQL actually treats every SQL statement as being executed within a transaction. If you do not issue a BEGIN command, then each individual statement has an implicit BEGIN and (if successful) COMMIT wrapped around it. A group of statements surrounded by BEGIN and COMMIT is sometimes called a transaction block

这里还有一个叫做 savepoint 的概念,在mysql中没有这个东西,pg的事务处理中,可以设置savepoint,以允许设置rollback的位置。使用 roolback to savepoint 可以保留savepoint之前的修改。一个示例:

BEGIN;
UPDATE accounts SET balance = balance - 100.00
WHERE name = 'Alice';
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00
WHERE name = 'Bob';
-- oops ... forget that and use Wally's account
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00
WHERE name = 'Wally';
COMMIT;

窗口函数

  • avg
  • rank
  • sum

这部分和MySQL中很不一样,提供了更丰富的特性。吃饭去了,下次再写吧。

安装

被蓝屏搞没了
默认的initdb数据目录是 /var/lib/pgsql/data/

简介

被蓝屏搞没了

入门

重写

创建数据库

pg的管理模式和mysql很不一样。在MySQL我们要登陆到mysql服务器才能进行创建数据库,添加用户等操作。但是pg提供了一个createdb 的命令,可以直接在bash中创建数据库。但是要求: 运行该命令的用户必须是启动postgresql服务的用户。

使用yum安装的pg会自动创建一个postgres用户并用这个用户启动相关的各个线程,所以要运行createdb 命令,需要先切换到postgres 用户。由于是系统创建的用户,我们也不知道用户的密码是什么,可以先重置 postgres 的密码。

sudo passwd postgres
// enter new password here

su postgres
createdb golang
// dropdb golang

当然也可以使用命令参数指定用户

createdb -U postgres golang

但是运行并没有成功,还是没有权限,切换到postgres用户比较好用。这样的管理方式对于习惯mysql的人来说,多少有些奇怪。

创建pg用户

在登陆管理pg之前我们要学会怎么创建pg的用户,并用这个用户去登陆pg的服务。
说实话 官方文档 看起来可不是那么简单。
首先要切换到系统的postgres用户,登陆pg创建新用户。

su postgres
psql

postgres =# create user golang with login password 'golang'
postgres =# \q

这样创建一个用户名为golang,登陆密码为golang的可登录用户。但是切换回普通用户,登陆还是会报错。

su wuxu
psql -U golang -W
Enter password
psql: FATAL: Ident authentication failed for user "golang"

命名用户名和密码都是对的,却总是认证失败,注意前面的Ident authentication是什么意思?
这牵涉到pg的认证方式了。我们知道pg的认证方式是保存在配置文件 pg_hba.conf 中,这个文件在初始化数据(initdb) 所制定的目录,默认地址是 /var/lib/pgsql/data/pg_hba.conf ,这是一个比较标准的Linux风格的配置文件,一行代表一条规则。主要内容如下:

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local all all peer
# IPv4 local connections:
host all all 127.0.0.1/32 ident
# IPv6 local connections:
host all all ::1/128 ident

我们关心的是最后那个字段,METHOD,就是认证的方式,有很多种方式可以配置,常用的

  • trust 任何连接都允许,不需要密码
  • reject 拒绝符合条件(前面几个条件)的请求
  • MD5 接收一个MD5加密过的密码
  • password 接收一个密码来登陆,只在可信的网络使用这种方式
  • gss 使用gssapi认证,只在tcp/ip连接可用
  • sspi 只在windows可用的一种方式
  • krb5 不常用,只在TCP/IP可用
  • ident 使用操作系统用户名认证,验证它是否符合请求的的数据库用户名
  • ldap 使用LDAP服务器认证
  • cert 使用ssl客户端认证
  • pam 使用操作系统的pam模块服务

根据上面的报错,是配置文件使用了ident认证方式,而用户golang并不是系统用户,所以会认证失败。所以我们要添加一行认证配置:

还有可能系统监听了 v4/v6两个端口,需要使用 -h 127.0.0.1 指定ipv4,否则会连接ipv6的端口,也会认证失败

host	golang	golang	0.0.0.0		password

然后重新加载配置文件 sudo systemctl reload postgresql.service.这样应该就能使用golang用户登录了。

psql

psql是pg的中孤单交互程序,就像mysql的mysql程序一样。如下登陆: psql -U golang -h 127.0.0.1 -p 5432 -W,然后输入密码进入交互界面。

进入控制台有一些sql标准之外的命令,这些命令常用来管理pg。它们使用 \ 开头,常用命令如下:

  • \h:查看SQL命令的解释,比如\h select。
  • \?:查看psql命令列表
  • \l:列出所有数据库
  • \c [database_name]:连接其他数据库
  • \d:列出当前数据库的所有表格
  • \d [table_name]:列出某一张表格的结构
  • \du:列出所有用户
  • \e:打开文本编辑器
  • \conninfo:列出当前数据库和连接的信息
  • \password 设置密码
  • \q 退出

应该说pg的控制台比mysql的控制台人性化很多,特别是旧版本的mysql控制台真的很难用,不过5.6,或者mariadb 的控制台也有很大的改善了。

可以和mysql控制台一样在psql控制台运行各种标准sql语句。#注意字符串使用单引号# j建表,增删改查没有太大的区别。
官方给的创建表语句(我加了一个自增的字段,–表示注释):

CREATE TABLE weather (
id SERIAL primary key, -- same as auto_increments
city varchar(80),
temp_lo int, -- low temperature
temp_hi int, -- high temperature
prcp real, -- precipitation
date date
);
CREATE TABLE cities (
name varchar(80),
location point
);

pg支持位置的point数据类型,它是一对数据,代表经纬度,这样插入数据可以如下:

insert into cities values ('beijing', '(100.0, 36.0)');

或者要从txt文件导入数据,在mysql中,我们使用load data local infile ‘’ into table ** 这样的命令,pg使用copy命令导入:

copy table_name from '/path/to/file/of/server/'

一些对表结构的操作如下

# 添加栏位 
ALTER TABLE user_tbl ADD email VARCHAR(40);
# 更新结构
ALTER TABLE user_tbl ALTER COLUMN signup_date SET NOT NULL;
# 更名栏位
ALTER TABLE user_tbl RENAME COLUMN signup_date TO signup;
# 删除栏位
ALTER TABLE user_tbl DROP COLUMN email;
# 表格更名
ALTER TABLE user_tbl RENAME TO backup_tbl;
# 删除表格
DROP TABLE IF EXISTS backup_tbl;

在生产环境中,不推荐使用 select * 这样的查询语句,因为结果与表结构关联,表插入一栏后结果会改变。

联合查询

多表联合查询实际上和mysql也没有太多区别,也就是标准的sql。

聚合函数

Pg支持很多聚合函数,其使用和mysql并灭有太多区别。比如max.注意的是max不能用在where子句。

select max(tmp_lo) from weather;
select city from weather where tmp_lo = max(temp_lo); // 错误
select city from weather where temp_lo = (select max(temp_lo) from weather); // 正确

另一个常用的聚合就是group by了。没什么特殊的,和mysql同样的用法,注意group by 的条件子句使用 having condition 就可以了。

WHERE selects input rows before groups and aggregates are computed, whereas HAVING selects group rows after groups and aggregates are computed. Thus, the WHERE clause must not contain aggregate functions; it makes no sense to try to use an aggregate to determine which rows will be inputs to the aggregates. On the other hand, the HAVING clause always contains aggregate functions. (Strictly speaking, you are allowed to write a HAVING clause that doesn’t use aggregates, but it’s seldom useful. The same condition could be used more efficiently at the WHERE stage.)

参考

done

反射是提供一种程序在运行时检查变量结构,状态的能力,它是元编程的一种形式(metaprogramming)。反射功能强大,但是很多时候反射使得代码难以理解,并且性能并不很好。

类型和接口

反射功能是基于类型系统(type system),所以要先对Go 的类型有足够的认识。我们都知道Go是静态类型的,即使是像type MyInt Int 这样定义的类型,虽然MyInt和Int有相同的基础类型(underlying type),但是MyInt和Int还是不一样的,不能互相赋值。

接口是一类重要的特殊类型,它代表的是一系列方法,一个接口声明的变量可以用于存储任何实现了该接口方法的变量。常见的接口如io.Reader和 io.Writer 。

var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)

在使用中,经常需要明确知道r中到底存储的是什么类型的变量(concrete value)。由于Go是静态类型的,r的类型永远是 io.Reader

阅读全文 »

Go官方并不提供数据库驱动,而只是为开发数据库驱动定义了一些标准接口。我们要使用mysql数据库得使用第三方维护的mysql驱动。Go中支持MySQL的驱动目前比较多,有如下几种,有些是支持database/sql标准,而有些是采用了自己的实现接口,常用的有如下几种:

https://github.com/go-sql-driver/MySQL 支持database/sql,全部采用go写。
https://github.com/ziutek/mymysql 支持database/sql,也支持自定义的接口,全部采用go写。
https://github.com/Philio/GoMySQL 不支持database/sql,自定义接口,全部采用go写。
也推荐使用第一个,主要理由:

  • 维护的比较好
  • 完全支持database/sql接口
  • 支持keepalive,保持长连接

有了驱动,其实golang使用mysql和其他语言没有太大的区别,虽然具体的方法调用与类组织不太一样。

准备

使用10.4.16.95上的mariadb数据库,新建一个golang的数据库,添加两张测试用的表。

create database golang;
use golang;

CREATE TABLE `userinfo` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(64) DEFAULT NULL,
PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `userdetail` (
`uid` int(11) NOT NULL DEFAULT '0',
`intro` varchar(128) DEFAULT NULL,
`profile` text
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

先ssh登录服务器(因为安全考虑,不开放mysql的root用户的远程登录权限),用root用户本地登录mysql新建一个用于测试的可远程登录的用户:

ssh wuxu@10.4.16.95
MySQL -uroot -p
// enter root password
create user golang@'%' identified by 'golangMysql';
grant privileges on *.golang to golang@'%';
flush privileges;

这样就可以用golang登录新建的数据库了

Golang操作mysql

先get数据库驱动,在命令行运行go get github.com/go-sql-driver/MySQL,会自动使用git从github下载mysql驱动工程到$GOPATH/src/

基本的插入,更新,删除,查找方法如下:

import (
_ "github.com/go-sql-driver/MySQL"
"database/sql"
"fmt"
)

func main() {
// mysql是一个注册过的数据库驱动了,在驱动的init函数中注册
db, err := sql.Open("MySQL", "golang:golangMysql@tcp(10.4.16.95:3306)/golang?charset=utf8")
CheckErr(err)
fmt.Println("connected")

stmt, err := db.Prepare("insert userinfo set username=?")
CheckErr(err)

// fmt.Println(stmt)
res, err := stmt.Exec("wuxu") // 使用占位符'?'防止sql注入,这是一个不定参数函数,传入的参数与prepare阶段设置的占位符相等
CheckErr(err)

id, err := res.LastInsertId()
CheckErr(err)

fmt.Println(id)

stmt, _ = db.Prepare("update userinfo set username=? where username=?")

res, err = stmt.Exec("wuxu01", "wuxu")
aff, err := res.RowsAffected()
CheckErr(err)

fmt.Println(aff)

rows, err := db.Query("select * from userinfo")

for rows.Next() {
var id int
var usename string
err = rows.Scan(&id, &usename)
CheckErr(err)

fmt.Println(id, usename)
}

stmt, _ = db.Prepare("delete from userinfo where uid>?")
res, _ = stmt.Exec(4)
aff, _ = res.RowsAffected()
fmt.Println("affected", aff)
}

func CheckErr(e error) {
if e != nil {
panic(e)

}
}

与以往的mysql操作相似,也是一个连接对象,对于插入,删除等要先prepare,然后执行Exec,在exec阶段绑定占位符变量。
比较独特的是Exec返回的Result指针,它包含LastInsertId和 RowsAffedted 两个方法。

注意数据连接的dsn(data source name)和php或者其他语言的比较不一样。我们使用的是 user:password@protocol(host:port)/dbname?charset=utf8 的形式。驱动本身支持更多形式的dsn:

  • user@Unix(/path/to/socket)/dbname?charset=utf8
  • user:password@tcp(localhost:5555)/dbname?charset=utf8
  • user:password@/dbname
  • user:password@tcp([::]:80)/dbname // ipv6

整个数据库操作的流程大概如下:

sql.Open()函数用来打开一个注册过的数据库驱动
db.Prepare()函数用来返回准备要执行的sql操作, 然后返回准备完毕的执行状态。
db.Query()函数用来直接执行Sql返回Rows结果。
stmt.Exec()函数用来执行stmt准备好的SQL语句。

数据库驱动的注册在数据库驱动包导入时执行,注册的逻辑写在包的init函数中。导入驱动包只需要 import _ "driver/package",因为只需要执行其init(),而不需要调用包中导出的方法,调用的是go语言规定的sql接口。

HTTP

我们知道,go内置有http server 支持,我们只需要在代码中启动这个server就可以启动一个类似于apache的服务器。同时可以很方便的监听多个端口等等。

import (
. "fmt"
"net/http"
)

const (
PORT = ":1024"
MSG = "hello, gopher"
)
func main() {
http.HandleFunc("/hello", Hello)
go func() {
http.ListenAndServe(PORT, nil)
}()

go func() {
http.ListenAndServeTLS(":443","cert.pem", "key.pem", nil)
}()
for {}
}
func Hello(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
Fprintf(w, MSG)
}

这段代码启动了一个server,开启了http和https支持;分别监听在1024端口和443端口。
有几个需要注意的地方。

阅读全文 »

指针和值

自定义类型的实例化对象有值类型和指针类型的区别(个人理解),比如下面:

type Unicorn struct {
Name string
Age int
weight int
}
larry := new(Unicorn) // *Unicorn, pointer type
larry := &Unicorn{} // 这和上面一样

la := Unicorn{} // value type

定义函数

定义函数很简单:

func message(name string) string {
message := fmt.Sprintf("hello, %v", name)
return message
]
阅读全文 »

LVM 是一种可用在Linux内核的逻辑分卷管理器;可用于管理磁盘驱动器或其他类似的大容量存储设备。

LVM的特点

首先LVM有如下的优点:

  • 文件系统可以跨多个磁盘,因此不会受物理磁盘大小的限制
  • 可以在系统运行状态下扩展文件系统大小
  • 可以增加新的磁盘到LVM的存储池中
  • 调整逻辑卷大小的时候不需要考虑逻辑卷的位置,不需要担心没有足够的连续空间
  • 可以以镜像的方式冗余重要数据到多个物理磁盘上
  • 可以很方便的导出整个卷组并写到另一台机器上

当然LVM也存在一些限制:

  • 卷组中一个磁盘损坏时,整个卷组都受影响
  • 不能减小文件系统大小
  • 存储性能会受到影响
  • 从卷组中移除一个磁盘时必须使用reducevg
阅读全文 »

经常会忘记mysql的root密码,尤其是机器一多的时候。上周五把科委的服务器迁移到centos上面,当时把mysql配置好了,也能登录了,但是今天各省测试的时候,发现mysql链接失败,登录服务器用命令行登录发现是 “Access Denied”,明明记得是正确的密码,又不能登录了,估计又记错了。

经常会忘记这些密码,干脆记录一下怎么样重置密码吧。我们知道mysql的用户信息是保存在mysql的一张表里面的,如果要重置密码则需要登录到mysql的服务器。按照mysql的官方文档,可以使用不需要密码的方式启动mysql服务器,这样就能不需要密码登录了。

先关闭mysql

sudo service mysqld stop

然后启动无权限表限制的服务

sudo mysqld_safe --skip-grant-tables

登录mysql, 设置密码

MySQL
set password for 'root'@'localhost' = PASSWORD('yourPassword');

注意设置密码需要使用PASSWORD函数处理,也就是把密码hash存储。

重新启动正常的服务

sudo kill `cat /var/run/mysqld/mysqld.pid`  // 也可以通过ps查找pid再kill
sudo service mysqld start

当然,其他账号同理可以修改密码了。注意不要开启root的远程登录权限。
done

参考: https://dev.MySQL.com/doc/refman/5.0/en/resetting-permissions.html