工程中心第一周学习总结
这一周学习的一些随笔主要是围绕数据库和Redis
姜云瀚_第一周学习总结
一、MySQL主从复制原理和实现
MySQL主从复制- 负载平衡
2.备份
3.容灾恢复。
3.mysql支持的复制类型:
1):基于语句的复制: 在主服务器上执行的SQL语句,在从服务器上执行同样的语句。MySQL默认采用基于语句的复制,效率比较高。 一旦发现没法精确复制时, 会自动选着基于行的复制。
2):基于行的复制:把改变的内容复制过去,而不是把命令在从服务器上执行一遍. 从mysql5.0开始支持
3):混合类型的复制: 默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制。
两台Linux主机下完成MySQL主从复制。
主服务器:
开启二进制日志
配置唯一的server-id
获得master二进制日志文件名及位置
创建一个用于slave和master通信的用户账号
从服务器:
配置唯一的server-id
使用master分配的用户账号读取master二进制日志
启用slave服务
测试用(用室友PC和本人PC做的实验,连的WiFi,确定两台PC能够ping通)
master:192.168.43.103/linux
slave:192.168.43.8/linux
1.修改master和slave 的my.cnf配置文件
[mysqld]#主数据库
log-bin=mysql-bin
server-id=1
----------------------------
[mysqld]#从数据库
server-id=2
2. 创建同步用的用户
创建用户并授权
CREATE USER 'slave1'@'192.168.43.8' IDENTIFIED BY '123';
GRANT REPLICATION SLAVE ON *.* TO 'slave1'@'192.168.43.8';
flush privileges;
3.查看master状态
查看master二进制文件名,和pos具体位置,确定slave同步时的参数
SHOW MASTER STATUS;
4.slave执行同步SQL语句(需要master主机名,用户名,密码,二进制文件的名称和位置):
CHANGE MASTER TO
MASTER_HOST='192.168.43.103',
MASTER_USER='slave1',
MASTER_PASSWORD='123',
MASTER_LOG_FILE='mysql-bin.000002',
MASTER_LOG_POS=154;
5.启动slave同步进程
START SLAVE;
查看slave状态如果
SHOW SLAVE STATUS \G;
(Slave_IO_Running和Slave_SQL_Running都为YES的时候就表示主从同步设置成功了)我刚开始出现了slave_IO_Running:connecting的错误,Last_IO_Errno: 2003 ,一直连接不上,网上查找了一下,才知道是主库防火墙没关导致的。
service iptables stop
然后重启一次slave,成功。测试了一下,在主库新建表插入数据后,在从库也新增该数据。
二、Redis
redis是当前使用最广泛的NOSQL之一,性能远超数据库,主要运用在两种主要的场景,一是缓存常用数据,二是在高并发的情况下,对数据进行高速的读/写。
1.1redis当作缓存
redis缓存应用当第一次读取数据的时候,读取redis的数据会失败,此时会读取数据库,把数据读取出来,并写入redis,当第二次以后读取数据时,就可以直接从Redis中读取出数据。因为redis基于内存的读/写,要比基于磁盘的数据库读/写快得多,所以提升了整体的读写速度(这让我想起了CPU、cache和内存的关系)
1.2高速读/写的情景
高速读写业务2.Redis数据结构和常用命令
redis分为6种数据结构string、list、hash、set、zset、HyperLogLog(基数)。6种数据结构常见命令如下表:
2.1 string
key和value都是string
命令 | 作用 |
---|---|
set key value | 设置键值对 |
get key | 通过key获取value |
del key | 通过key删除键值对 |
strlen key | 求value字符串长度 |
getset key value | 修改key对应的value |
getrange key start end | 获取字串 |
append key value | 添加新字符串到原value后面 |
2.2 hash
命令 | 作用 |
---|---|
HDEL key field1 | 删除一个或多个哈希表字段 |
HEXISTS key field | 查看哈希表 key 中,指定的字段是否存在 |
HGET key field | 获取存储在哈希表中指定字段的值 |
HGETALL key | 获取在哈希表中指定 key 的所有字段和值 |
HINCRBY key field increment | 为哈希表 key 中的指定字段的整数值加上增量 increment |
HINCRBYFLOAT key field increment | 为哈希表 key 中的指定字段的浮点数值加上增量 increment 。 |
HKEYS key | 获取所有哈希表中的字段 |
HLEN key | 获取哈希表中字段的数量 |
HMGET key field1 field2 | 获取所有给定字段的值 |
HMSET key field1 value1 field2 value2 | 同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
HSET key field value | 将哈希表 key 中的字段 field 的值设为 value 。 |
HSETNX key field value | 只有在字段 field 不存在时,设置哈希表字段的值 |
HVALS key | 获取哈希表中所有值 |
2.3 list
redis中的list是一个双向链表
2.4 set
集合
2.5 zset
有序集合,基于hash结构的存储,key、value、score。对score进行排序
2.6 HyperLogLog
基数,评估需要的空间单元数
3. Redis在Java中
3.1 redis java API
工程中导入jedis.jar,Java连接Redis测试Redis读写性能
(注:今晚师兄提示此段代码要改成多线程模拟多用户环境)
package com.redis.Test;
import redis.clients.jedis.Jedis;
/*
* java连接redis测试读写速度
*@author 姜云瀚
*多次测试结果为两万次/秒左右
*/
public class JavaConncetRedis {
public static void main(String[] args) {
public static void main(String[] args) {
// TODO Auto-generated method stub
Jedis jedis=new Jedis("localhost",6379);
int i = 0;
long start=System.currentTimeMillis();
try{
while(true){
long end=System.currentTimeMillis();
if(end-start>=1000)break;
jedis.set("test_key"+i, "value"+i);
i++;
}
}finally{
jedis.close();
}
System.out.println("1秒钟操作"+i+"次");
}
}
3.2 spring中操作redis数据结构
spring中提供了序列化,将Java对象和redis中存储的字符串相互转换。对象序列化需要继承Serializable接口,spring中使用RedisTamplate操作redis。需要导入spring-data-redis.jar(这个包和spring版本以及jedis.jar版本容易冲突,折腾好久,spring版本可以用4.3.2,spring-data-redis可用1.7.2)。
spring中配置RedisTamplate,主要配置连接池、连接工厂、序列化器
applicationContext.xml
<context:property-placeholder location="classpath:redis.properties" />
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis."maxTotal}" />
<property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
</bean>
<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/>
<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="keySerializer" ref="stringRedisSerializer"></property>
<property name="valueSerializer" ref="stringRedisSerializer"></property>
</bean>
redis.properties
redis.host=localhost
redis.port=6379
redis.pass=
redis.maxIdle=50
redis.maxTotal=100
redis.maxWaitMillis=20000
RedisStringTest.java
public class RedisStringTest {
private static ApplicationContext applicationContext;
public static void main(String[] args) {
applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
RedisTemplate<String, String> redisTemplate=(RedisTemplate<String, String>)applicationContext.getBean(RedisTemplate.class);
redisTemplate.opsForValue().set("key1", "value1");
redisTemplate.opsForValue().set("key2", "value2");
redisTemplate.opsForValue().set("key3", "value3");
System.out.println(redisTemplate.opsForValue().get("key1"));
redisTemplate.delete("key1");
String oldvalue=(String) redisTemplate.opsForValue().getAndSet("key2", "value2new");
System.out.println("key2旧值"+oldvalue);
System.out.println("key2新值"+redisTemplate.opsForValue().get("key2"));
String rangevalue2=redisTemplate.opsForValue().get("key2", 0, 2);
System.out.println(rangevalue2);
long newlen=redisTemplate.opsForValue().append("key3", "_new");
System.out.println("key3 长度 is"+newlen);
System.out.println("key3 新值 is"+redisTemplate.opsForValue().get("key3"));
}
对list、hash、set、zset等操作类似。
RedisSetTest.java
public class RedisSetTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
RedisTemplate<String, String> redisTemplate=applicationContext.getBean(RedisTemplate.class);
Set set=null;
redisTemplate.boundSetOps("set1").add("v1","v2","v3");
redisTemplate.boundSetOps("set2").add("v2","v3","v4");
System.out.println(redisTemplate.opsForSet().size("set1"));
set=redisTemplate.opsForSet().difference("set1", "set2");
set=redisTemplate.opsForSet().intersect("set1", "set2");
boolean exit=redisTemplate.opsForSet().isMember("set1", "v1");
String str1=redisTemplate.opsForSet().pop("set1");
set=redisTemplate.opsForSet().members("set1");
redisTemplate.opsForSet().union("set1", "set2");
redisTemplate.opsForSet().differenceAndStore("set1", "set2", "diff_set");
redisTemplate.opsForSet().intersectAndStore("set1", "set2", "inter_set");
}
}
RedisHashTest.java
public class RedisHashTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
RedisTemplate<String, String> redisTemplate=applicationContext.getBean(RedisTemplate.class);
Map<String, String> map=new HashMap<>();
map.put("f1", "val1");
map.put("f2", "val2");
redisTemplate.opsForHash().putAll("key1", map);
redisTemplate.opsForHash().put("key1", "f3", 10);
boolean exit=redisTemplate.opsForHash().hasKey("key1", "f1");
System.out.println(exit);
redisTemplate.opsForHash().increment("key1", "f3", 5);
List<Object>list=redisTemplate.opsForHash().values("key1");
redisTemplate.opsForHash().delete("key1", "f2");
}
}
3.3 redis基础事务
命令 | 作用 |
---|---|
multi | 开启事务,之后的命令进入队列,不会马上执行 |
watch key | 监听某些key,如果在事务执行之前被修改,则事务会回滚 |
unwatch key | 取消监听key |
exec | 执行事务 |
discard | 回滚事务 |
3.4redis流水线
redis流水线Redis的流水线功能允许客户端一次性将多个命令发送给服务器,并将被执行的多个命令请求的结果在一个命令回复中全部返回给客户端,使用这种功能可以有效地减少客户端在执行多个命令时需要与服务器进行通信的次数。提高系统性能。
下面是测试流水线的速度
(注:今晚师兄提示此段代码要改成多线程模拟多用户环境)
package com.redis.test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
/*开启流水线操作
*测试速度为每秒约20万次
*/
public class RedisPipeline {
public static void main(String[] args) {
// TODO Auto-generated method stub
Jedis jedis=new Jedis("localhost",6379);
Pipeline pipeline =jedis.pipelined();
long start=System.currentTimeMillis();
int i=0;
while(true){
long end=System.currentTimeMillis();
if(end-start>=1000)break;
pipeline.set("test_key"+i, "value"+i);
i++;
}
System.out.println("1秒钟操作"+i+"次\"");
}
}
3.5发布订阅
命令 | 作用 |
---|---|
subscribe | 订阅给定的一个或多个频道的信息 |
publish | 将信息发送到指定的频道 |
unsubscribe | 退订指定的一个或多个频道 |
命令 | 作用 |
---|---|
subscribe | 订阅给定的一个或多个频道的信息 |
publish | 将信息发送到指定的频道 |
unsubscribe | 退订指定的一个或多个频道 |
3.6 超时命令
命令 | 作用 |
---|---|
persist key | 移除key的超时时间 |
ttl key | 查看key的超时时间 |
expire key second | 设置超时时间戳 |
pexpire key | 设置键值的超时时间 |
两种方式回收超时键值对:
定时回收
惰性回收
6种回收策略
volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集中任意选择数据淘汰
allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
allkeys-random:从数据集中任意选择数据淘汰
no-enviction:默认不淘汰任何键值对。
3.7 lua语言脚本
EVAL script numkeys key [key ...] arg [arg ...]
4. Redis配置
redis主从复制流程
全量同步
增量同步
Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
Redis 数据备份与恢复
127.0.0.1:6379> SAVE
恢复数据
将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务。
哨兵模式
哨兵模式5.spring、redist、数据库
5.1. redis和数据库结合
缓存注解 | 作用 |
---|---|
@Cacheable | 进入方法之前,先查找缓存中是否有缓存值,若没有找到则执行方法,若没找到则调用方法,最后将key的结果保存到缓存中 |
@CachePut | 直接执行方法,然后将结果存到缓存中 |
@cacheEvict | 移除缓存中key对应值 |
读操作
数据缓存在redis上会设置超时时间,当redis中的数据超时后,redis就不能读出该数据了,这个时候会读取数据库,然后将数据库中的数据写入redis,这样在读取的过程中就能按一定时间刷新redis中的数据。
写操作
image.png
使用spring缓存机制整合redis和mybatis
定义Role.java 、RoleMapper.java、RoleService.java
配置mybatis、配置RedisTemplate、配置RedisCacheManager(这个主要是配置缓存名、超时时间等)
spring中的缓存机制注解:@EnableCaching启动缓存机制
缓存注解 | 作用 |
---|---|
@Cacheable | 进入方法之前,先查找缓存中是否有缓存值,若没有找到则执行方法,若没找到则调用方法,最后将key的结果保存到缓存中 |
@CachePut | 直接执行方法,然后将结果存到缓存中 |
@cacheEvict | 移除缓存中key对应值 |
RoleService.java
package com.redis.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.redis.dao.RoleMapper;
import com.redis.pojo.Role;
@Service
public class RoleService {
@Autowired
private RoleMapper roleMapper=null;
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED)
@Cacheable(value="redisCacheManager",key="'role_'+#id")
public Role getRole(long id) {
return roleMapper.getrole(id);
}
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED)
@CachePut(value="redisCacheManager",key="'role_'+#result.id")
public Role insertRole(Role role) {
roleMapper.insertrole(role);
return role;
}
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED)
@CachePut(value="redisCacheManager",key="'role_'+#result.id")
public Role updateRole(Role role) {
roleMapper.updaterole(role);
return role;
}
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED)
@CacheEvict(value="redisCacheManager",key="'role_'+#result.id")
public long deleRole(long id) {
return roleMapper.delerole(id);
}
}
本周总结
看书的时候没有练习,等到练习的时候已经忘了一部分内容了,下周要边看边练习。
下周学习计划
复习Java基础,复习ssm并实现ssm+redis抢红包的高并发业务来巩固。完成多台主机下redis哨兵模式主从配置。