1.mysql原始命令
a.登录
登录:
mysql -u root -p密码 //连本机的
SELECT User, Host, Password FROM mysql.user; //查看用户
添加新用户:
create user 'liuxuan14'@'localhost' identified by '123456';
修改密码:
set password for 'liuxuan14'@'localhost'=password('1998');
为用户授权:
//允许访问所有数据库下的所有表
grant all privileges on *.* to '新用户名'@'指定ip' identified by '新用户密码' ;
//指定数据库下的指定表
grant all privileges on test.test to '新用户名'@'指定ip' identified by '新用户密码' ;
//只拥有查询权限
grant select on *.* to '新用户名'@'指定ip' identified by '新用户密码' WITH GRANT OPTION;
FLUSH PRIVILEGES; //刷新权限
删除用户:
drop user liuxuan@localhost;
连接远程数据库:
MySQL 连接远程数据库(192.168.5.116),端口“3306”,用户名为“root”,密码“123456”
mysql -h 192.168.5.116 -P 3306 -u root -p123456
mysql -h ip -P 端口 -A -u name -p 密码
create database name
b.建表
CREATE TABLE IF NOT EXISTS `mytable`(
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '自增',
`app_id` bigint NOT NULL DEFAULT '0' COMMENT '应用id',
`time` int NOT NULL DEFAULT '0' COMMENT '时间',
`date` DATE,
PRIMARY KEY ( `id` )
UNIQUE KEY `idx_app` (`app_id`) USING BTREE
KEY `idx_ip` (`machine_ip`) USING BTREE,
KEY `idx_app_machineid` (`app_id`,`machine_id`) USING BTREE
)ENGINE=InnoDB DEFAULT COMMENT='表名';
AUTO_INCREMENT定义列为自增的属性,一般用于主键,数值会自动加1。
PRIMARY KEY关键字用于定义列为主键。 您可以使用多列来定义主键,列间以逗号分隔。
ENGINE 设置存储引擎,CHARSET 设置编码。
表结构:desc name;
建表语句:show create tablename;
注释
注释:comment
create table test1 (
field_name int comment '字段的注释'
)comment='表的注释';
修改表的注释:
alter table test1 comment '修改后的表的注释';
修改字段的注释:
alter table test1 modify column field_name int comment '修改后的字段注释';
查看表注释:
show create table test1;
查看字段注释:
show full columns from test1;
索引
唯一索引:
UNIQUE KEY
1 主键是一种约束,唯一索引是一种索引,两者在本质上是不同的。
2 主键创建后一定包含一个唯一性索引,唯一性索引不一定就是主键。
3 唯一性索引列允许空值, 而主键列不允许为空值。
4 主键可以被其他表引用为外键,而唯一索引不能。
5 一个表最多只能创建一个主键,但是可以创建多个唯一索引。
6 主键更适合那些不容易改变的唯一标识,如自动递增列,身份证号等。
7 在RBO 模式下,主键的执行计划优先级高于唯一索引。两者可以提高查询的速度。
c.添加一个字段
alter table tablename add `app_name` varchar(16) NOT NULL DEFAULT '';
d.修改数据
update tablename set 字段='修改的值' where 条件;
多个字段中间加,
e.删除
删库:drop database name;
删表:drop table name;
mysql删除一行:
delete from 表 where 条件;
f.添加一行
insert into tablename (app_id,machine_id,machine_ip,ctime,use_perc) values(?,?,?,?,?); //对应字段加
insert into 表名 values(int,'string')(..); //可一次性加多行,一行全部数都有
insert into 表名 set 列名='', ...;
2.go操作数据库
使用:database/sql包
mysql默认端口号3306
查看:mysql内,show global variables like 'port';
查看mysql配置文件目录:
mysql --help|grep 'my.cnf'
a.创建连接池
//1.创建连接池
func newPool() *sql.DB { //sql.DB 表示一个连接池
cfg := mysql.NewConfig()
cfg.User = "name"
cfg.Passwd = "123456"
cfg.Net = "tcp"
cfg.Addr = "127.0.0.1:3306"
cfg.DBName = "mydb"
dsn := cfg.FormatDSN()
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
if err := db.Ping(); err != nil { //ping测试网络连通性及用户密码是否正确
log.Fatal(err)
}
return db
}
b.query查询
func QueryUser(id int64) (*User, error) {
var db = newPool() //创建连接
rows, err := db.Query("select `id`, `name` from `users` where `id` = ?", id)
if err != nil {
return nil, err
}
defer rows.Close() // 注意这里,一定要关闭
user := User{}
for rows.Next() {
if err := rows.Scan(&user.ID, &user.Name); err != nil {
return nil, err
}
break
}
if err := rows.Err(); err != nil {
return nil, err
}
return &user, nil
}
连接池对程序员是透明的,这里并不需要显式的从连接池里获取连接,而是通过连接池来执行查询语句。
Query 方法返回一个 *Rows 指针,代表结果集。
要注意的是 defer rows.Close() 如果忘了关闭,可能会造成连接泄露。
rows.Scan 方法有个方便的特性,如果id在数据库里是 varchar(50) 类型,我们传的参数&user.ID指向int64,这依然可以工作,Scan 方法会执行自动转换。
c.单行查询queryrow
// QueryUser1 单行查询
func QueryUser1(id int64) (*User, error) {
row := pool.QueryRow("select `id`, `name` from `users` where `id` = ?", id)
user := User{}
if err := row.Scan(&user.ID, &user.Name); err != nil {
if err == sql.ErrNoRows {
return nil, nil // 返回 (*User)(nil) 表示查询结果不错在
}
return nil, err
}
return &user, nil
}
d.插入更新删除exec
// InsertUser 插入用户
func InsertUser(name string) (int64, error) {
res, err := pool.Exec("insert into `users` (`name`) values (?)", name)
if err != nil {
return 0, err
}
return res.LastInsertId()
}
// UpdateUser 更新用户
func UpdateUser(id int64, name string) error {
_, err := pool.Exec("update `users` set `name` = ? where `id` = ?", name, id)
if err != nil {
return err
}
return nil
}
// DeleteUser 删除用户
func DeleteUser(id int64) error {
_, err := pool.Exec("delete from `users` where `id` = ?", id)
if err != nil {
return err
}
return nil
}
e.事务
// UpdateFooBar 更新
func UpdateFooBar(id int64, x, y string) (err error) {
tx, err := pool.Begin()
if err != nil {
return
}
defer func() {
switch {
case err != nil:
tx.Rollback()
default:
err = tx.Commit()
}
}()
_, err = tx.Exec("update `foo` set `x` = ? where `id` = ?", x, id)
if err != nil {
return
}
_, err = tx.Exec("update `bar` set `y` = ? where `id` = ?", y, id)
if err != nil {
return
}
return
}
3.mysql加锁
读锁全表锁(LOCK TABLE 表 READ)
读锁行锁(SELECT ... LOCK IN SHARE MODE)
写锁全表锁(LOCK TABLE 表 WRITE)
写锁行锁(SELECT ... FOR UPDATE)
锁总体可以分为乐观锁和悲观锁,简单说,乐观锁用版本号控制,悲观锁用锁控制。
乐观锁:核心原理是增加一个version的字段来控制。添加一个version字段,每个更新时where条件都加上它,并且也更新它UPDATE users SET name="雪山飞猪",version=version+1 WHERE id=3 AND version=0 UPDATE users SET name="chenqionghe",version=version+1 WHERE id=3 AND version=0。这就是最简单的CAS机制。
悲观锁:类似Go语言里的Mutex和RwMutex
共享锁(S锁)又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S 锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
排他锁(X锁)又称写锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。
写锁表锁:
加锁:lock table kiss_share_idproducer write;
此时其他进程无法读也无法写。
解锁:unlock table;
--------------------------------
写锁行锁:
begin;
加锁:select * from kiss_share_idproducer where app_id=21 for update;
其他再加锁,等待,直到占锁的commit;
更新:update kiss_share_idproducer set mix_id=7 where app_id=21;
解锁:commit;
读锁表锁:
加锁:lock table kiss_share_idproducer read;
此时其他进程可以读但无法写。
解锁:unlock table;
-------------------------------
读锁行锁:
加锁:select * from kiss_share_idproducer where app_id=21 lock in share mode;
其他再加锁,等待,直到占锁的commit;
更新:update kiss_share_idproducer set mix_id=7 where app_id=21;
解锁:commit;
参考
go的mysql操作:https://blog.csdn.net/embinux/article/details/84031620
mysql加锁:https://blog.csdn.net/weixin_33838871/article/details/113566444